**题目描述:给出1个数N,统计0~N内0-9出现的次数。
问题看起来并不是很难,最简单的方法是从1开始遍历到N,将其中每一个数的位数中各含有的数加起来,自然就得到了从1-N各个数的和。但是如果给定数字比较大时,比如100000000,则求取速度非常慢。那能不能找到一个更快的方法解决这个问题尼?
仔细分析这个问题,给定一个数N,先分析1-N每个数各个位数上“1”个数的和,对于一个特定的数N,试分析其规律。
设F(N)=y//1-N的数中“1”的个数
先看一位数的情况
分析N=4,从1-4的数字中,只有1的个位上出现1,所以F(4)=1,进而发现只有一位数的情况下,F(N)=1(N>=1),如果N=0,则F(0)=0。
再分析两位数的情况
分析N=14,从1-14的数字中,个位数是1的数字有1、11。而十位数有1的数有10、11、12、13、14,其中11重复被计算了两次,所以不用重复考虑(11中刚好含有两个“1”)。
再分析N=24,从1-24的数字中,个位数有1的数字有1、11、21,十位数有1的数有10、11、12、13、14、15、16、17、18、19。可以看出当N>=20时,十位数为“1”的个数为10。
分析规律可得,两位数的情况下,个位数为1的次数不仅与1有关,还与十位数有关:如果个位数大于等于1,则个位上有1的数字等于十位的数字加1,如果个位的数字等于0,则个位上有1的数字等于十位的数字;十位上出现1的次数也类似,如果十位数等于1,则十位上有1的数字等于个位的数字+1,如果十位上的数字大于1,则十位上出现1的数字等于10。
则可总结出规律:
F(10)=个位出现1的个数+十位出现1的个数=1+1=2
F(13)=个位出现1的个数+十位出现1的个数=2+4=6
F(23)=个位出现1的个数+十位出现1的个数=3+10=13
·············································
F(93)=个位出现1的个数+十位出现1的个数=10+10=20
根据上述的公示,可推导到一般公式
如果要计算百位数上1的数的个数,则有三个因素影响:百位以上的位数上的数、百位上的数、百位以下的数。
若百位上的数为0,比如13023,则百位上为1的数为:100-199,1100-1199,2100-2199·············12100-12199共1300个数,也就是由更高位数字(13)决定。并且等于更高位数乘当前位数。
若百位上的数为1,比如13123,则百位上为1的数为100-199,1100-1199,2100-2199·············12100-12199,13100-13123共1324个数,也就是由更高位和更低位决定。等于更高位乘当前位数+低位数加1。
若百位上的数大于1,则百位上为1的数等于更高位数加1后乘当前位数。
好了,这样就可以求出1-N中1的个数,其他数大同小异,但在求取0的个数时,只有两种情况:
若百位上的数为0,则百位上为0的数为更高位-1后乘当前位数(数的最高位为0无意义);若百位上的数大于0,则百位上为0的数等于更高位数乘当前位数
贴上代码
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int fun(int i)//1`9数量
{
int cns=1,pro,now,low,count=0;;
while(n/cns!=0)
{
pro=n/(cns*10);
now=(n/cns)%10;
low=n-(n/cns)*cns;
if(now<i) count+=pro*cns;
else if(now==i) count+=pro*cns+low+1;
else count+=(pro+1)*cns;
cns*=10;
}
return count;
}
int fun1()//0的数量
{
int cns=1,pro,now,low,count=0;;
while(n/cns!=0)
{
pro=n/(cns*10);
now=(n/cns)%10;
low=n-(n/cns)*cns;
if(now==0) count+=(pro-1)*cns+low;
else count+=pro*cns;
cns*=10;
}
return count;
}
int main()
{
cin>>n;
cout<<fun1()<<endl;
for(int i=1;i<10;i++)
{
cout<<fun(i)<<endl;
}
}