编程之美 2.4 “1”的数目及扩展问题

来源:互联网 发布:深圳市国税开票软件 编辑:程序博客网 时间:2024/05/22 14:32

问题描述:


      给定一个十进制整数N,求出从1到N的所有整数中出现”1”的个数。 

     例如:N=2,1,2出现了1个“1”。

               N=12,1,2,3,4,5,6,7,8,9,10,11,12。出现了5个“1”。


解题思路:

1.计算从1到N经历的每个数中1的个数相加

   其中数字i(1=<i<=N)中1的数目:分解出i的个位、十......最高位,分别判别是否等于1,等于则总数目加1

#include <iostream>using namespace std;typedef unsigned long long int mm;mm numberof1(mm N){mm num=0;for(mm i=1;i<=N;i++){mm m=0,j=i;while(i!=0){m=i%10;if(m==1)num++;i=i/10;}i=j;}return num;}

     此程序时间复杂度:


    编程之美书上写的是: 

   此程序很简单,需注意:求i各个位上数的过程中会破坏i,故要在求之前保存好i,在判别完后恢复i。


2.使用列举法寻找规律,进行数学归纳,用代码表示  

   归纳过程不再赘述,编程之美书上有,这里更正书里的一个错误:135页倒数第二段最后一句,114应改为14,123+1改为13+1。

    归纳规律为:十进制表示第i为出现1的次数与第i位上的数字有关,设第i位上的数为p,i位以上的数字为高位数字,以下的数字为低位数字,如1700030,对于第三位来说,高位数字为1700,低位数字为30,则p满足以下规则:

     p=0    高位数字*(10^(i-1))

     p=1     高位数字*(10^(i-1))+低位数字+1

     p=2   (高位数字+1)*(10^(i-1))

    程序如下:

mm quicknumof1(mm N){mm low=0,cur=0,high=0,num=0,factor=1;while((N/factor)!=0){low=N-(N/factor)*factor;cur=(N/factor)%10;high=(N/factor)/10;switch(cur){case 0:num+=high*factor;break;case 1:num+=high*factor+(low+1);break;default:num+=(high+1)*factor;break;}factor*=10;}return num;}
      程序中注意

     1.要准确的求取低位数字、当前位数字、高位数字;

    2.每个case之后要break,降低时间复杂度

    3.记得factor要累乘,从而target不同位

    此程序的时间复杂度为N的十进制位数,即 


改进:

   对于上述分类归纳,可以进一步归纳,可以看出,无论p为几,该位“1”的个数均包括高位数字*(10^(i-1)),P不同时附加的值不同。P为0或1,附加的值是(低位数字+1)*p;p为其他,附加的值是10^(i-1)。

    在程序中,只需要定义变量addnum,利用条件表达式,addnum=p>1?10^(i-1):(低位数字+1)*p;从而避免了繁琐的switch语句。


扩展问题:

    给定一个十进制整数N,求出从1到N的所有整数二进制表达中出现”1”的个数。

    对于这个问题,我们同样枚举二进制表达每一位上出现“1”的次数,总结归纳,可得:

    第i位上“1”出现的次数=高位*(2^(i-1))+第i位数字+低位数字

    程序为:

mm quicknumofbinary1(int N){int lowdigit=0,curdigit=0,highdigit=0,num=0,factor=1;while((N/factor)!=0){lowdigit=N-(N/factor)*factor;curdigit=(N/factor)%2;highdigit=(N/factor)/2;num+=highdigit*factor+curdigit+lowdigit;factor*=2;}return num;}

  此程序的时间复杂度为N的二进制位数,即 




 
原创粉丝点击