题解:洛谷 P1554 梦中的统计
【题目来源】
【题目描述】
Bessie 的大脑反应灵敏,仿佛真实地看到了她数过的一个又一个数。她开始注意每一个数码(\(0\dots 9\)):每一个数码在计数的过程中出现过多少次?
给出两个整数 \(M\) 和 \(N\),求在序列 \([M,M+1,M+2,\dots, N-1,N]\) 中每一个数码出现了多少次。
【输入】
第 \(1\) 行:两个用空格分开的整数 \(M\) 和 \(N\)。
【输出】
第 \(1\) 行:十个用空格分开的整数,分别表示数码 \(0\dots 9\) 在序列中出现的次数。
【输入样例】
129 137
【输出样例】
1 10 2 9 1 1 1 1 0 1
【核心思想】
-
问题分析:给定一个整数区间 \([M, N]\),要求统计在这个区间内所有整数中,数码 \(0\) 到 \(9\) 各自出现的总次数。本质是一个数位统计问题,可以通过枚举区间内每个数并拆分其各位数字来实现。
-
算法选择:
- 枚举 + 数位拆分:遍历从 \(M\) 到 \(N\) 的每一个整数,对每个整数通过不断取模和整除 10 的方式拆出每一位数字,并累加到对应的计数数组中。该方法的时间复杂度为 \(O((N-M+1) \times \log_{10} N)\),对于题目给定的数据范围(通常 \(N-M \le 10^5\))是足够的。
- 适用场景:当区间长度不大时,这种直接模拟的方法简单可靠,是入门题目的标准解法。
-
关键步骤:
- 读入与初始化:读取区间端点 \(M\) 和 \(N\);初始化一个大小为 10 的计数数组
cnt[0..9],全部置 0。 - 枚举区间内每个数:使用
for (int i = M; i <= N; i++)循环。 - 数位拆分:对于当前数字
i,使用一个临时变量x = i,然后循环while (x > 0),每次取出x % 10作为当前数位,将cnt[digit]++,然后x /= 10。 - 处理数字 0:上述循环对数字 0 本身不会处理(因为 0 不进入 while 循环),但题目要求统计数码 0 的出现次数。如果
M = 0,则需要额外将cnt[0]加 1。另一种更通用的做法是使用do-while循环确保每个数字至少处理一次。 - 输出结果:按顺序输出
cnt[0]到cnt[9],用空格分隔。
- 读入与初始化:读取区间端点 \(M\) 和 \(N\);初始化一个大小为 10 的计数数组
-
时间/空间复杂度:
- 时间复杂度:\(O((N-M+1) \times \log_{10} N)\),其中 \(\log_{10} N\) 是数字的位数(最多 10 位),效率很高。
- 空间复杂度:\(O(1)\),仅使用常数大小的计数数组。
-
模拟与数位拆分:
- 核心思想:将问题分解为对每个独立数字的处理。数位拆分是处理数字各位问题的基本技术。
- 边界处理:需要注意当
i = 0时,while循环不会执行,需要特殊处理。代码中while (n1 != 0)循环在n1 = 0时不会计数 0,这在统计从 0 开始的区间时会出现漏计,应使用do-while循环或单独判断。 - 适用场景:这类统计数字各位出现次数的问题是数位统计的入门形态,适合初学者理解枚举和取模运算。
【解题思路】

【算法标签】
入门 #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int main()
{
int m, n, a[15]={0}; // 定义m、n和a数组,a数组初始化为0
cin >> m >> n; // 输入m和n
for (int i=m; i<=n; i++) { // 从m遍历到n
int n1 = i; // 得到每个数
while (n1!=0) { // 进行数位拆分
int t = n1 % 10; // 取出余数
n1 = n1 / 10; // 每个数再整除10,取出余数部分
a[t]++; // 使用投票箱方法,对t下标的数据进行自增
}
}
for (int i=0; i<=9; i++) { // 依次遍历0-9
cout << a[i] << " "; // 打印每个数字出现的次数,中间用空格隔开
}
return 0;
}
【运行结果】
129 137
1 10 2 9 1 1 1 1 0 1
浙公网安备 33010602011771号