题解:洛谷 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

【核心思想】

  1. 问题分析:有 \(n\) 名候选人(编号 1 到 \(n\)),收到 \(m\) 张选票,每张选票上有一个候选人编号。需要将所有选票编号按从小到大排序并输出。由于候选人编号范围很小(\(n \le 999\)),而选票数量 \(m\) 可能很大(\(\le 2 \times 10^6\)),因此可以使用计数排序(也称为桶排序)在 \(O(n+m)\) 时间内完成。

  2. 算法选择

    • 计数排序:创建一个大小为 \(n+1\) 的数组 count,初始化为 0。遍历所有选票,对于每张选票编号 \(t\),执行 count[t]++。然后按编号从小到大,根据 count[i] 的值输出对应数量的编号。
    • 适用性:计数排序是本题的最优选择,因为编号范围很小,且 \(m\) 很大,可以避免使用 \(O(m \log m)\) 的基于比较的排序。
  3. 关键步骤

    • 读入:读取 \(n\)\(m\)
    • 统计:循环 \(m\) 次,读取每个选票编号 t,并增加 count[t]
    • 输出:遍历 \(i\) 从 1 到 \(n\),对每个 \(i\),输出 count[i]\(i\),用空格分隔。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n+m)\),读入 \(m\) 个数字,然后输出 \(m\) 个数字。
    • 空间复杂度:\(O(n)\),计数数组大小。
  5. 计数排序

    • 核心思想:利用值域有限的特点,直接统计每个值出现的次数,然后按顺序输出。这种方法比快速排序等基于比较的算法更快,尤其适合 \(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
posted @ 2026-08-31 09:24  团爸讲算法  阅读(15)  评论(0)    收藏  举报