51nod:数字0-9的数量(c++算法)

**题目描述:给出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;
	}

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值