51nod 1009 数字1的数量

题目来源:51nod1009

题目大意:输入一个十进制数N,计算出从0到N所有数里所有1的个数(注:111里有3个1)

【冷静分析】这道题乍一看有些棘手(实际也不是水题),我们不妨先找找规律。

·一位数里一共有几个1?

答:1。一个。

·两位数里有几个1?

答:个位数是1的:1,11,21,31,……(10个)

十位数是1的:10,11,12,13……(10个)

所以有二十个。

·三位数呢?

答:我不想列举了,有三百个。

扫描二维码关注公众号,回复: 3456289 查看本文章

等等:1,20,300……

……??????!似乎真的有规律啊!

也就是说,从0——9999(k个9)一共有1的个数是:

f(k)=k(10^k)————①*

惊人的发现啊!我们继续找规律。

不妨以12345这个数为例:

由结论①可知,在1——9999内有4000个1

接下来看10000——12345

我们惊奇地发现,这2346个数的万位都是1,所以答案加上2346

顺着这个思路,我们再来看千位:

当千位为0时,后面的三个数无论是多少,整体都不会超过12345

我们自然想到结论①,这0——999中有300个1

当千位为1时,后面三个数无论是多少,整体也不会超过12345

再加上300,当然,不能忘了千位的1,它一共贡献了1000

当千位为2时,抱歉,路不通了

那我们再看百位。同理:

百位可以固定的数是0,1,2,它们总共会贡献3 * f(2)= 60

百位上的1可以贡献: 100

看十位:固定0,1,2,3,贡献4 * f(1)= 4

十位上的1可以贡献: 10

个位可以是0,1,2,3,4,5,贡献: 1

所以答案是8121

怎么设计程序呢?

首先要注意到的是函数f,可以预处理一下,用数组保存每一个k的f[k]

剩下来的操作虽然冗长,但并不繁杂,涉及到的操作如下:

1、若正在处理的数字是1,计算出1后面的数字m,ans+=m+1(回想加2346的操作)

2、若处理的是个位,若个位不是0,则ans++

3、若正在处理的数字c不是个位也不是1,从0枚举到c-1,ans+=f[s-1](s表示这一位的位数,个位是1,十位是2……)

不要忘了加上这一位上1贡献的数,也就是10 ^ (k-1)

标程如下

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
ll f[10];
ll n,ans;

void solve(ll t,int s)
{
	if(t==1){//处理个位 :1234?中?出现1的可能性 
		for(ll i=0;i<=n%10;i++){
			if(i==1)  ans++;
		}
		return;//证明处理完了 
	}
	ll now=(n/t)%10;//当前正在处理位置上的数字 
	if(now==1)  ans+=n%t+1;//正在处理位置上的数是1:
	//从10000到12345万位上一共可以贡献2346个1
	for(ll i=0;i<now;i++){//这一位上可以固定的数 (如果是1就无效了)
		if(i==1)ans+=t;//这一位可以贡献的1的个数 
		ans+=f[s-1];//s表示这一位是从右往左第几位 
	}
	solve(t/10,s-1);//处理下一位,相应地,s应该右移 
}


int main()
{
	ll a,b=1,c=1;
	for(a=1;a<=9;a++){
		f[a]=a;
		for(ll i=1;i<a;i++)f[a]*=10;
	}
	scanf("%lld",&n);
	while(b<n)b*=10,c++;
	solve(b,c);
	printf ("%lld\n",ans);
	return 0;
 }

猜你喜欢

转载自blog.csdn.net/M_oisture/article/details/81070296