PAT A1049 Counting Ones (30 分)

本文详细解析了PATA1049题目“CountingOnes”,介绍了如何计算从1到N范围内数字1出现的总次数。通过分析数字的组成结构,提出了有效的计算策略,并对比了两次代码提交的不同之处,展示了算法优化的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

1049 Counting Ones (30 分)

题意

1 ∼ N 1 \thicksim N 1N 1 1 1 的个数

分析

设数字 N N N k k k 位,第 i i i 位为数字 b b b 。其前面组成数字 a a a ,后面组成数字 c c c
n k − 1   n k − 2 …   n i + 2   n i + 1 ⏞ a   n i b   n i − 1   n i − 2 …   n 1   n 0 ⎵ c \overbrace{n_{k-1}\ n_{k-2}\dots\ n_{i+2}\ n_{i+1}}^{a}\ \overset{b}{n_i} \ \underbrace{n_{i-1}\ n_{i-2}\dots\ n_1\ n_0}_c nk1 nk2 ni+2 ni+1 a nib c ni1 ni2 n1 n0
因为 n i n_i ni 的情况有3种:

  1. b > 1 b>1 b>1 ,则 b b b 前面数字的范围可以是 ( 0 , a ) (0,a) (0,a) a + 1 a+1 a+1 个,由于 b > 1 b>1 b>1 b b b 之后的数字随意,选取范围为 ( 0 , 1 0 i − 1 ) (0,10^i-1) (0,10i1),总共 ( a + 1 ) × 1 0 i (a+1)\times 10^i (a+1)×10i 种。
  2. b = 1 b=1 b=1 ,前面的范围比 c a s e 1 case1 case1 少一种,因为前面的数为 a a a 时后面的数只有 ( 0 , c ) (0,c) (0,c) 满足 b = 1 b=1 b=1,总共 a × 1 0 i + c + 1 a\times 10^i+c+1 a×10i+c+1种。
  3. b = 0 b=0 b=0,比 c a s e 1 case1 case1 前面范围完全少一种,故 a × 1 0 i a\times 10^i a×10i 种。

i i i 位为 1 1 1 的个数为 f ( i ) = { ( a + 1 ) × 1 0 i , b > 1 ; a × 1 0 i + c + 1 , b = 1 ; a × 1 0 i , b = 0. f(i)=\begin{dcases} (a+1)\times 10^i,&b>1;\\ a\times 10^i+c+1,&b=1;\\ a\times 10^i,&b=0. \end{dcases} f(i)=(a+1)×10i,a×10i+c+1,a×10i,b>1;b=1;b=0.
累加即可

初次提交代码

#include<cstdio>
#include<cmath>
#pragma warning(disable:4996)

int main() {
	int a, b, c = 0, ans = 0;
	scanf("%d", &a);
	for (int i = 0; a; i++) {//a为0退出
		b = a % 10;
		a /= 10;
		if       (b > 1) ans += (a + 1) * pow(10, i);
		else if  (b < 1) ans += a * pow(10, i);
		else     ans += a * pow(10, i) + c + 1;
		c += b * pow(10, i);
	}
	printf("%d\n", ans);
	return 0;
}

运行结果

2019/4/24 16:13:33 答案正确 30 1049 C++ (g++) 3 ms
测试点 结果 耗时 内存
0 答案正确 2 ms 384 KB
1 答案正确 2 ms 384 KB
2 答案正确 2 ms 296 KB
3 答案正确 3 ms 384 KB
4 答案正确 2 ms 384 KB
5 答案正确 2 ms 256 KB
6 答案正确 2 ms 256 KB

改进1

可以改用变量 k 来代替 pow(10,i) 同时可节省 i 的变量空间,for 改为 while。

#include<cstdio>
#pragma warning(disable:4996)

int main() {
	int n,a, b, c, k = 1, ans = 0;
	scanf("%d", &n);
	while (n / k != 0) {
		a = n / (k * 10);
		b = n / k % 10;
		c = n % k;
		if (b > 1)      ans += (a + 1) * k;
		else if (b < 1) ans += a * k;
		else            ans += a * k + c + 1;
		k *= 10;
	}
	printf("%d\n", ans);
	return 0;
}

  1. 《算法笔记》P198-200. ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值