编程之美 2.4 “1”的数目

题目是这样的:给定一个正整数N,从1到N一共出现过多少个1?

如N=12,则f(12)=5,因为1,2,3,4,5,6,7,8,9,10,11,12共出现5次“1”。

当年第一次看这个题的时候觉得无从下手,这次到迅速有了思路,还是有进步的嘛。

思路就是分别统计个位、十位、百位…上的1的数目。先考虑个位上的1的数目,个位上的1是每10个数中会出现1次的,即1-10出现一次,11-20出现一次…因此可以计算N/10,对于N%10的部分,只要N%10>=1,就有一个1,也只能有一个1。同理再考虑十位上的1,十位上的1是每100个数字中会出现10次,如1-100出现10次(10-19),101-200出现10次(110-119)…因此可以计算N/100*10,对于N%100的部分,只有在10-19之间会出现1,因此可以分情况讨论。百位上、千位上的情况依此类推,再举一例,说一下千位上的情况吧,对于千位上的1,是每10000个数里出现1000次,例如1-10000里出现1000次(1000-1999),10001-20000里出现1000次(11000-11999)…因此可以计算N/10000*1000,对于N%10000的部分,只有在1000-1999之间会出现千位上的1,同样分情况讨论之。

代码如下:

   1:  __int64 func(__int64 N)
   2:  {
   3:      __int64 onesCount = 0;
   4:      __int64 factor = 10;
   5:      __int64 a,b,c;
   6:      do 
   7:      {
   8:          a = N/factor;
   9:          b = N%factor;
  10:          c = a*(factor/10);
  11:   
  12:          onesCount += c;
  13:   
  14:          if (b >= factor/10)
  15:          {
  16:              if (b > 2*(factor/10) - 1)
  17:              {
  18:                  onesCount += factor/10;
  19:              }
  20:              else
  21:              {
  22:                  onesCount += b - (factor/10) + 1;
  23:              }
  24:          }
  25:          factor *= 10;
  26:      } while (a != 0);
  27:   
  28:      return onesCount;
  29:  }

有一个有意思的现象是,对于N=111…110这样的数,即a个1加一个0组成的数,其f(N)恰好等于连续a个(a+1),如f(1110)=444,f(11111110)=8888888。以f(1110)=444为例,一共有四位,每一位上的一都是111个,因此f(1110)=444。

扩展问题问到了对于2进制的情况该怎么解答,例如f(11)=100,因为“0,10,11”中共出现了四个1。其实思路是一样的,也是分别考察右侧第一位上出现1的次数、右侧第二位上出现1的次数…以此类推。比如考虑f(11)=f(1011),

1,10,11,100,101,110,111,1000,1001,1010,1011

先考虑第一位上的1(第几位一律从右侧数起),第一位上的1是每两个数出现一次,因此可以先计算11/2=5,然后对于11%2=1的部分,由于等于1,因此出现了一次1,因此第一位上共出现了6次1;再考虑第二位,这一位上是每4个数出现2次1,先计算11/4*2=4,对于11%4=3部分,只有在10-11之间会出现2次1,因此第二位上出现6次1;第三位,(11/8*4)+0=4次,第四位,0+(11-7)=4。因此f(1011)=10100=20。代码就不写了吧。

    原文作者:super_chris
    原文地址: https://blog.csdn.net/super_chris/article/details/6537051
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞