题目是这样的:给定一个正整数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。代码就不写了吧。