[PAT-A 1049]Counting Ones

在这里插入图片描述
题目大意:
给出一个数字n(n<=2^30),求1-n里面出现1的个数。
样例中在1-12内出现的1的数字又1,10,11,12,总计5个1。

思路:
如果暴力一个一个枚举,必然超时,应当从右向左拆分数字,统计每个位出现1的个数,最后累加。
在这里插入图片描述
记当前位数字now,now所处位数为a,ans为结果
1)如果now=0,则ans+=left*a.
2)如果now=1,则ans+=left*a+1+right.
3)如果now>=2,则ans+=(left+1)*a.

样例解释
对n=30710逐位分析
1)a=1时,now=0,left=3071,right=0.
此时在a=1即各位上出现1的个数的情况为XXXX1。
其中当XXXX为0000-3070,共3071(left)种。
2)a=10时,now=1,left=307,right=0
此时在a=10即十位上出现1的个数情况为XXX1X,
其中当XXX为000-306共307(left)种,右边X可以取值为0-9(a种),共left*a=3070种。
当XXX为307时,因为n=30710,now=1,所以只有一种取值30710。
共left*a+1+right=3071种。
3)当a=100时,now=7,left=30,right为10
此时a=100时即百位上出现1的情况为XX1XX
左边取值范围为00-30,对右边任意低2位(00-99),now均可以取到1.
共l(eft+1)*a=(30+1)*100=3100种。
4)当a=1000,now=0,left=3,right=710
此时a=1000时即千位上出现1的情况位X1XXX
因为now=0,左边取值范围为0-2是右边XXX的范围可以为000-999,共a种,当左边为3时,不可能出现千位上为0的情况。
共left*a=3000种
5)当a=100000,now=3,left=0710,right=0
此时a=10000即万为上出现1的情况为1XXXX。
XXXX的取值可以为0000-99999,共a种
所以共(1+left)*a=10000种。
上面累计结果得到22242种。

总结来说
当now=0时,左边只有取值在[0,left-1]时该位才有可能为1,当取值为[0,left-1]时,右边right可以任意取,即[0,a-1]种情况。
当now=1时,分为两种情况
1)同now=0时,左边取值在[0,left-1]时,右边可以取到[0,a-1]任意,共left*a个1
2)左边取值为left时,now=1,此时右边取值只能为[0,right],如30715,当now=1,a=10时,
如果left取到307,则前四位为3071,最后一位只能为[0,5]中任意一位,即right+1种。
所以,这种情况,总体取值为left*a+right+1个1。
当now>=2时,左边可以取[0,left]任意值,now都可以到达1,如30715种,当now=3,a=100时,
left可以取[0,307]中的任意值,每一种对应的right可以任意取,即[0,a-1]种情况。共(left+1)*right个1。

AC代码:

//PAT_A 1049
#include<cstdio>
using namespace std;
int main() {
	int n, a = 1, ans = 0;
	int left, right, now = 0;
	(void)scanf("%d", &n);
	while (n / a != 0) {
		left = n / (a * 10);
		now = n / a % 10;
		right = n % a;
		if (now == 0)ans += left * a;
		else if (now == 1)ans += left * a + right + 1;
		else ans += (left + 1) * a;
		a *= 10;
	}
	printf("%d", ans);
	return 0;
}
发布了142 篇原创文章 · 获赞 1 · 访问量 4580

猜你喜欢

转载自blog.csdn.net/weixin_44699689/article/details/104267756