PAT A 甲级 1049 Counting Ones (30分)

1049 Counting Ones (30分)

思路

完全的数学题。
看到N的取值的那刻我就放弃用枚举手段了,所以也没有验证会不会超时。大概是会的,不然这题也太没难度了。

言归正传。这道题的重点是找到一个计算方法。方法正确的话,步数会下降到可以人类手算的程度——换句话说就是可以忽略不计(迫真奥数题)。

那么,怎么统计呢?
作为举例,假设这个数是9位数,也就是xab cde fgh。
很显然,对于任何一个9位数的n,我们要计算的范围首先必然包括1~99 999 999。
对于它们,首先范围变成0~99 999 999,然后把所有数扩充成八位,也就是00 000 000到99 999 999。此时你会发现,所有由8个数字组成的数(任何一位的取值都是0到9)都在这个范围内了。这一堆数字中总共有100 000 000*8个数字,而1的出现概率即是十分之一,故0到99 999 999的数字范围内1出现了80 000 000次。

同样的,从k00 000 000到k99 999 999间,不考虑k的话,1也应该出现了80 000 000次。

所以,对于xab cde fgh,不考虑x位的话,从0到x00 000 000,1出现了x8pow(10,8)次。

再来看x位。显然如果x不是1(大于等于2),那么x位1就出现了100 000 000到199 999 999共pow(10,8)个数,每个各1次,如果x就是1,那就是100 000 000到1ab cde fgh共ab cde fgh+1次。

所以,现在,我们已经搞清了0~x00 000 000间1的出现次数和x所在位1的出现次数。只需要再加上x00 000 000到xab cde fgh间除了x所在位外1的出现次数。

而这很显然和0~ab cde fgh间1的出现次数是相同的。

所以,只需要把以上代码写成循环,然后现在把n的最高位去掉、把位数下调,然后再不断进行即可。

代码

#include<iostream>
#include<math.h>

using namespace std;

int main()
{
	long int n;//虽然其实int就足以放下题设n的最大值,但由于下方的那个循环条件退出时pow(10,i)是比n高一位的,而pow(10,i)的数据类型似乎与与之比较的n是相同的,故若n为int会溢出出错。(这是我的推测。不过确定的是用int最后一个测点会报答案错误。)
	cin >> n;
	int count = 0;
	int i = 0, k = 0;
	while (pow(10,i) <= n)//此处的条件
	{
		i++;
	}
	i--;
	while(i >= 0)
	{
		k = pow(10, i);
		count += (n / k)*i * k / 10;
		if (n / pow(10, i) >= 2)
			count += k;
		else
			count += n - k + 1;//注意这一句。后面会说明。

		n = n % k;
		while (pow(10, i) > n)
			i--;//注意如果不做这个循环直接i--的话,上面那一句将有可能得到负数结果,使得答案错误。当然也可以通过把else改成else if(n / pow(10, i)==0)规避。
	}

	cout << count;
	return 0;
}

题目

The task is simple: given any positive integer N, you are supposed to count the total number of 1’s in the decimal form of the integers from 1 to N. For example, given N being 12, there are five 1’s in 1, 10, 11, and 12.

Input Specification:
Each input file contains one test case which gives the positive N (≤2
​30
​​ ).

Output Specification:
For each test case, print the number of 1’s in one line.

Sample Input:
12

Sample Output:
5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值