一、题目
给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数。 要求: 写一个函数 f(N) ,返回1 到 N 之间出现的 “1”的个数。例如 f(12) = 5。 在32位整数范围内,满足条件的“f(N) =N”的最大的N是多少。
二、设计思路
思路来自于张欢龙的启发,就是将一个整数拆分,一位一位地求出“1”的个数,如451就是:
45*1+1*1
+4*10+1*10
+0*100+1*100。
先考虑个位,个位上数字0~9,总共出现过45个0~9,一个0~9有一个1,所以位45*1,而45个0~9外还有一个0~1,这时要再加上一个1;接着十位,每100个数里面均有*10到*19这10个出现1的数,所以十位也是0~9,但这里面的1要乘以10,4个0~9还多余一个0~5,所以也要加1,且乘以10;以此类推......
如果某一位上是1,是不是情况就不一样了?如111,111情况如下:
11*1+0+1
+1*10+1+1
+0*100+11+1
...
+(m前面的十进制值*10m+m后面的十进制值+1(后面的值全为0的情况))
就是当1出现时,这个多余的1的个数就为这一位后面的值。
求f(N)=N的情况时,从2147483647开始向下逐一计算,时间过长,随便选了一个数即214748364,用前面的算法求出结果发现f(N)>N,所以正确的N一定比这个数小,所以从这开始计算。
三、源代码
1 //刘双渤,2015年6月3日 2 //输入一个整数,输出出现1的个数 3 #include <iostream> 4 using namespace std; 5 6 void main() 7 { 8 long num,rem,t,S; //输入值,余数,十的倍数,一的总数 9 long numS; 10 cout<<"请输入一个整数:"<<endl; 11 cin>>num; 12 13 t = 1; 14 S = 0; 15 numS = num; 16 while(numS != 0) 17 { 18 rem = numS % 10; 19 numS = numS / 10; 20 if(numS != 0) 21 { 22 S += numS * t; 23 } 24 25 if(rem > 1) 26 { 27 S += t; 28 } 29 else if(rem == 1) 30 { 31 S = S + num -numS * t* 10 - rem * t + 1; 32 } 33 t = t * 10; 34 } 35 cout<<"出现的“1”的个数为:"<<endl; 36 cout<<S<<endl; 37 38 39 for(long i = 214748364;i >= -214748364;i--) 40 { 41 t = 1; 42 S = 0; 43 numS = i; 44 while(numS != 0) 45 { 46 rem = numS % 10; 47 numS = numS / 10; 48 if(numS != 0) 49 { 50 S += numS * t; 51 } 52 if(rem > 1) 53 { 54 S += t; 55 } 56 else if(rem == 1) 57 { 58 S = S + i -numS * t* 10 - rem * t + 1; 59 } 60 t = t * 10; 61 } 62 if(S == i) 63 break; 64 } 65 cout<<"满足条件的“f(N) =N”的最大的N是:"<<S<<endl; 66 }
四、实验结果
五、实验总结
刚看到这个题目时,感觉很简单,然后就开始一个个值去算,用归纳法找规律,可越写发现情况越复杂,有点难!课上有人展示思路,下课再想,就有慢慢清晰,简单了起来。方法很多,却很需要这灵光一点,才能通透。