首先容易想到的一种方法就是遍历这n个数,求出每个数中包含1的个数,然后加起来就ok了
- //从1 到 n的正数中1出现的次数
- #include <iostream>
- using namespace std;
- //求n中包含几个1
- int lmf(unsigned int n)
- {
- int sum=0;
- while(n)
- {
- if(n%10 == 1)
- {
- sum++;
- }
- n = n/10;
- }
- return sum;
- }
- int bxy(unsigned int n)
- {
- int sum = 0;
- for(unsigned int i=1; i <= n; ++i)
- {
- sum += lmf(i);
- }
- return sum;
- }
- void main()
- {
- cout << bxy(11) <<endl;
- }
但是这样做了好多无用功,试试对21345做个分析
首先把数字分为两部分:
1到1345
1346到21345
首先求从1346到21345
考虑最高位引入的1的个数,从10000到19999,一共10^4个,就是10的(length-1)次方。
由后四位引入的1的个数,计算如下:
2*10的3次方,
其中2指的是:万位上的两种选择0或者2
10的三次方指的是:后四位中有一位是1,其他三位从0到9十个数字任意选。
1到1345 使用递归求解。
程序如下:
- #include <iostream>
- using namespace std;
- int Pow10(unsigned int n)
- {
- int result = 1;
- for(unsigned int i=0; i<n; i++)
- {
- result *= 10;
- }
- return result;
- }
- int lmf(const char *strN)
- {
- if(!strN || *strN<'0' || *strN>'9' || *strN=='\0')
- {
- return 0;
- }
- int firstDigit = *strN-'0';
- unsigned int length = static_cast<unsigned int>(strlen(strN));
- //如果只有一位数,而且还是0,那就是0 了。。。
- if(length==1 && firstDigit==0)
- {
- return 0;
- }
- //如果只有一位数,但是大于0,那肯定包含一个1
- if(length==1 && firstDigit>0)
- {
- return 1;
- }
- //这里变量代表:10000到19999 这期间由于第一位引入的1的个数
- int numFirstDigit = 0;
- //如果最高位不是1,例如:23415,则由最高位引入的1的个数就是从10000到19999:也就是10^(5-1)
- if(firstDigit>1)
- {
- numFirstDigit = Pow10(length-1);
- }
- //如果第一位是1,例如12345.则由于第一位而引入的 1的个数就是从10000到12345,一共2346
- if(firstDigit == 1)
- {
- numFirstDigit = atoi(strN+1)+1;
- }
- //这个变量计算的是:由01346到21345除了第一位剩下其他为所引入的1的个数,
- //也就是说第一位只能取0或者2------firstDigit
- //剩下的四位中选出一位是1,即4种选法----------length-1
- //剩下的三位可以自由选择------------10^(length-2)
- //所以计算如下:
- int numOtherDigit = firstDigit * (length-1) * Pow10(length-2);
- //剩下的数字也就是从1到1345,递归调用此函数
- int numRecursive = lmf(strN+1);
- //返回三部分的和
- return numFirstDigit+ numOtherDigit +numRecursive;
- }
- int bxy(int n)
- {
- if(n <= 0)
- {
- return 0;
- }
- char strN[50];
- sprintf(strN,"%d",n);
- return lmf(strN);
- }
- void main()
- {
- cout << bxy(20) <<endl;
- }