题解:洛谷 P1059 明明的随机数
【题目来源】
洛谷:P1059 [NOIP 2006 普及组] 明明的随机数 - 洛谷(luogu.com.cn)
【题目描述】
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了 \(N\) 个 \(1\) 到 \(1000\) 之间的随机整数\((N\le 100)\),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
【输入】
输入有两行,第 \(1\) 行为 \(1\) 个正整数,表示所生成的随机数的个数 \(N\)。
第 \(2\) 行有 \(N\) 个用空格隔开的正整数,为所产生的随机数。
【输出】
输出也是两行,第 \(1\) 行为 \(1\) 个正整数 \(M\),表示不相同的随机数的个数。
第 \(2\) 行为 \(M\) 个用空格隔开的正整数,为从小到大排好序的不相同的随机数。
【输入样例】
10
20 40 32 67 40 20 89 300 400 15
【输出样例】
8
15 20 32 40 67 89 300 400
【核心思想】
-
问题分析:给定 \(N\) 个范围在 \(1\) 到 \(1000\) 之间的随机整数,需要完成去重(只保留每个数字出现一次)和升序排序。由于数字范围很小(\(1 \sim 1000\)),可以使用计数排序/桶排序的思想:用一个布尔或计数数组标记数字是否出现过,最后按顺序输出出现过的数字。
-
算法选择:
- 桶排序(计数数组):创建一个大小为 \(1001\) 的数组
a,初始化为 0。遍历 \(N\) 个数字,将a[数字]置为 1(或累加计数)。然后遍历数组,输出所有a[i] == 1的 \(i\),同时统计个数。 - 复杂度:时间复杂度 \(O(N + 1000)\),空间复杂度 \(O(1000)\)。
- 桶排序(计数数组):创建一个大小为 \(1001\) 的数组
-
关键步骤:
- 读入:读取 \(N\),然后循环 \(N\) 次读取每个数字
t,执行a[t] = 1。 - 统计:遍历 \(i\) 从 1 到 1000,若
a[i] == 1,则count++。 - 输出:先输出
count,再遍历输出所有a[i] == 1的 \(i\)。
- 读入:读取 \(N\),然后循环 \(N\) 次读取每个数字
-
时间/空间复杂度:
- 时间复杂度:\(O(N + 1000)\)。
- 空间复杂度:\(O(1000)\)。
-
桶排序/计数排序:
- 去重与排序一次性完成:利用数组下标天然有序的特性,标记出现过的数字,即可同时完成去重和排序。
- 适用场景:当数据范围较小且需要去重排序时,桶排序是最简单高效的方法。
【算法标签】
普及- #其他排序
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, m = 0, a[1005] = {0}, t; // n: 输入数据的个数,m: 统计不同数字的个数,a: 桶数组,t: 临时变量用于存储输入的数字
// 输入数据到桶中
cin >> n; // 输入数字的个数
for (int i = 1; i <= n; i++) {
cin >> t; // 输入一个数字
a[t]++; // 将对应的桶计数加1,表示该数字出现过
}
// 统计多少个桶中有数据
for (int i = 1; i <= 1000; i++) {
if (a[i]) { // 如果桶中有数据(即该数字出现过)
m++; // 不同数字的个数加1
}
}
// 输出不同数字的个数
cout << m << endl;
// 按照从小到大顺序,将有数据的桶的编号输出
for (int i = 1; i <= 1000; i++) {
if (a[i]) { // 如果桶中有数据
cout << i << " "; // 输出该数字
}
}
return 0;
}
【运行结果】
10
20 40 32 67 40 20 89 300 400 15
8
15 20 32 40 67 89 300 400
浙公网安备 33010602011771号