题解:洛谷 P1271 选举学生会
【题目来源】
洛谷:P1271 【深基9.例1】选举学生会 - 洛谷(luogu.com.cn)
【题目描述】
学校正在选举学生会成员,有 \(n(n\le 999)\) 名候选人,每名候选人编号分别从 \(1\) 到 \(n\),现在收集到了 \(m(m\le 2000000)\) 张选票,每张选票都写了一个候选人编号。现在想把这些堆积如山的选票按照投票数字从小到大排序。
【输入】
输入 \(n\) 和 \(m\) 以及 \(m\) 个选票上的数字。
【输出】
求出排序后的选票编号。
【输入样例】
5 10
2 5 2 2 5 2 2 2 1 2
【输出样例】
1 2 2 2 2 2 2 2 5 5
【核心思想】
-
问题分析:有 \(n\) 名候选人(编号 1 到 \(n\)),收到 \(m\) 张选票,每张选票上有一个候选人编号。需要将所有选票编号按从小到大排序并输出。由于候选人编号范围很小(\(n \le 999\)),而选票数量 \(m\) 可能很大(\(\le 2 \times 10^6\)),因此可以使用计数排序(也称为桶排序)在 \(O(n+m)\) 时间内完成。
-
算法选择:
- 计数排序:创建一个大小为 \(n+1\) 的数组
count,初始化为 0。遍历所有选票,对于每张选票编号 \(t\),执行count[t]++。然后按编号从小到大,根据count[i]的值输出对应数量的编号。 - 适用性:计数排序是本题的最优选择,因为编号范围很小,且 \(m\) 很大,可以避免使用 \(O(m \log m)\) 的基于比较的排序。
- 计数排序:创建一个大小为 \(n+1\) 的数组
-
关键步骤:
- 读入:读取 \(n\) 和 \(m\)。
- 统计:循环 \(m\) 次,读取每个选票编号
t,并增加count[t]。 - 输出:遍历 \(i\) 从 1 到 \(n\),对每个 \(i\),输出
count[i]次 \(i\),用空格分隔。
-
时间/空间复杂度:
- 时间复杂度:\(O(n+m)\),读入 \(m\) 个数字,然后输出 \(m\) 个数字。
- 空间复杂度:\(O(n)\),计数数组大小。
-
计数排序:
- 核心思想:利用值域有限的特点,直接统计每个值出现的次数,然后按顺序输出。这种方法比快速排序等基于比较的算法更快,尤其适合 \(n\) 较小而 \(m\) 较大的情况。
- 适用场景:当数据范围有限且需要排序时,计数排序是非常高效的选择。
【算法标签】
普及- #其他排序
【代码详解】
#include <bits/stdc++.h> // 包含标准库中的所有头文件
using namespace std; // 使用标准命名空间
int main()
{
int n, m, t, a[1005] = {0}; // 定义变量n, m, t和一个大小为1005的数组a,并初始化为0
cin >> n >> m; // 输入n和m,n表示数字的范围,m表示输入的数字个数
// 循环m次,读取每个数字t,并在数组a中对应的位置计数
for (int i = 0; i < m; i++) {
cin >> t; // 输入数字t
a[t]++; // 在数组a中,将t对应的位置的值加1
}
// 遍历数组a,从1到n,输出每个数字i,输出次数为a[i]的值
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= a[i]; j++) {
cout << i << " "; // 输出数字i,并在后面加一个空格
}
}
return 0; // 程序正常结束
}
【运行结果】
5 10
2 5 2 2 5 2 2 2 1 2
1 2 2 2 2 2 2 2 5 5
浙公网安备 33010602011771号