1049 Counting Ones

1049 Counting Ones (30point(s))

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

 

思路:

可以把问题转化为排列组合问题:正整数N是一个n位十进制数,假设现在有n个盒子,编号分别位1,2,3,... n,分别对应个位、十位、百位、、。而现在我们需要把数字0~9分别放到n个盒子中,要满足放进盒子后,组成的数小于等于N,问有多少种放法

用排列的思想来考虑问题

假设N=30710,有5个盒子

5号4号3号2号1号
30710

 

 

 

1⃣️ 1号盒子的数是1时,要让盒子组成的数<=N,

由于1号盒子的数是1,30710的1号位时0,1>0,那么2号~5号可以取 0000~3070,共3071种情况(因为1号盒子存放的是1,如果2~5号取3071,那么盒子组成的数就是30711>N,不符合,所以最多是3070)

2⃣️ 2号盒子的数是1时,要让盒子组成的数<=N,

由于2号盒子的数是1,30710的1号位时1,1==1,那么3号~5号取000~306时,共307种情况,1号可以取0~9共10^1种情况,所以一共有307*10=3070种取法

3号~5号 等于307时,1号只能取0,即30710

总共3070+1=3071种

3⃣️ 3号盒子的数是1时,要让盒子组成的数<=N,

由于3号盒子的数1,30710的4号位是7,而1<7,所以4号~5号盒子可以取 00~30共31种,1号~2号可以取00~99共10^2=100种情况,共31*100=3100种

4⃣️ 4号盒子的数是1时,要让盒子组成的数<=N,

由于4号时数时1,30710的4号位是0,而1>0,所以5号位可以取0~2共3种,1~3号位可以取000~999共10^3=1000种,共3*1000=3000种(5号盒子的数不能>2,否则组成的数>N)

总共有 3000种

5⃣️ 5号盒子的数是1时,要让盒子组成的数<=N,

由于5号盒子时1,30710的5号位时3,而1<3,所以1~4号位可以取 0000~9999共10^4=10000个数

所以把1放在1~5号盒子,共有3071+3071+3100+3000+10000=22242种情况,即共有22242个‘1’

由上面的红色字体可知,

(下面 k为i号右边的位数)

当N的第i号位>1时候,共有 (i号左边的数的可取个数)* (10^{^{k}}),

当N的第i号为=1时候,共有 (i号左边的数的可取个数) *  (10^{^{k}}) + ( i号右边的数的可取个数)

当N的第i号位<1时候,共有 (i号左边的数的可取个数) *  (10^{^{k}})

 

假设N的i号位左边的数为 left,右边的数为 right,中间的数为 now

now>1时,(left+1)* (10^{^{k}})

now=1时,(left)* (10^{^{k}}) + (right+1)

now<1时,(left)* (10^{^{k}})

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include<algorithm>
#include<vector>
#include<cstring>
#include<string>
#include<functional>
#include<sstream>
using namespace std;
typedef long long ll;


int main() {
	int n, p, len;
	int left, right, now;
	scanf("%d", &n);
	len = to_string(n).size();
	ll ans = 0;
	p = 1; // p = 10^k
	for (int i = 1; i <= len; i++) {
		right = n%p; // i号位右边的数
		now = (n / p) % 10; //i号位的数
		left = n / (p * 10);//i号位左边的数
		if (now > 1) {
			ans += (left + 1)*p;
		}
		else if (now == 1) {
			ans += left*p + right + 1;
		}
		else {
			ans += left*p;
		}
		p *= 10;
	}

	printf("%lld\n", ans);
	return 0;
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值