1049 Counting Ones(30分)

题目翻译:

给出一个数字n,求1~n的所有数字里面出现1的个数

题解思路:

如果采用暴力枚举绝对会T,所以就需要找规律,用数学的方法求解。直观地想法就是按位加和,比如说对于一个三位数,分别统计个位、十位、百位出现1的个数然后加和即可。

比如我们选取112这个三位数分析一下:

当我们的视角落到个位上时:(因为个位是0-9,所以我们只需要考虑个位前面的部分能让我们这个个位出现多少次1)

001

011

021

031

041

051

061

071

081

091

101

111

可见,一共出现了(左边部分+1)次,也就是(11+1)=12次1,因此个位为1的个数就是12。

当我们的视角落到十位上时:(因为十位是0-9,所以我们只需要考虑十位前面的部分以及十位后面的部分能让我们这个十位出现多少次1,注意现在前后两部分都对十位出现1的个数做出了限制)

为什么呢?我们可以写一下:

010

011

012

013

014

015

016

017

018

019

110

111

112 

注意因为十位为1,并没有超过2,所以若百位为1的时候,十位为1的情况只有110-112,也就是3次,那如果这个数是122呢,是不是就可以从110-119了;因此,当十位为1的时候,十位出现1的次数=(左部)*10+(右部)+1 。当十位大于1的时候,不用考虑右部的约束,就相当于是(左部+1)*10.

当我们的视角落到百位上时:(因为百位是0-1,所以我们只需要考虑后边部分的限制,也就是百位为1时,后边部分+1)

100

101

102

103

104

105

106

107

108

109

110

111

112

此时百位1的左部是0,右部为0-12,依然满足上述公式(左部)*100+(右部)+1 .

总结一下:

我们将数字划分为左部left、现在关注的位置now(只有一位)、右部right。pos表示当前位置1会出现的次数,a表示now所代表的位数(比如个位数、百位数等)。则有如下公式:

pos = left * a + now * (right + 1)  when now<=1

pos = (left + 1) * a                       when now>=2

也可以参考柳神的博客1049. Counting Ones (30)-PAT甲级真题(数学问题)

代码:

#include <iostream>
using namespace std;
int main() {
	int n, left = 0, right = 0, a = 1, now = 0, ans = 0;
	scanf("%d", &n);
	while (n / a) {
		left = n / (a * 10), now = n / a % 10, right = n % a;
		if (now <= 1) ans += left * a + now * (right + 1);
		else ans += (left + 1) * a;
		a = a * 10;
	}
	printf("%d", ans);
	return 0;
}

坑点:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值