首先容易想到的一种方法就是遍历这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;
}
本文介绍了一种高效算法来计算从1到给定整数N中数字1出现的总次数。通过将数字分解并利用递归,避免了传统方法中的重复计算,显著提高了算法效率。

2222

被折叠的 条评论
为什么被折叠?



