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

【核心思想】

  1. 问题分析:给定 \(N\) 个范围在 \(1\)\(1000\) 之间的随机整数,需要完成去重(只保留每个数字出现一次)和升序排序。由于数字范围很小(\(1 \sim 1000\)),可以使用计数排序/桶排序的思想:用一个布尔或计数数组标记数字是否出现过,最后按顺序输出出现过的数字。

  2. 算法选择

    • 桶排序(计数数组):创建一个大小为 \(1001\) 的数组 a,初始化为 0。遍历 \(N\) 个数字,将 a[数字] 置为 1(或累加计数)。然后遍历数组,输出所有 a[i] == 1\(i\),同时统计个数。
    • 复杂度:时间复杂度 \(O(N + 1000)\),空间复杂度 \(O(1000)\)
  3. 关键步骤

    • 读入:读取 \(N\),然后循环 \(N\) 次读取每个数字 t,执行 a[t] = 1
    • 统计:遍历 \(i\) 从 1 到 1000,若 a[i] == 1,则 count++
    • 输出:先输出 count,再遍历输出所有 a[i] == 1\(i\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N + 1000)\)
    • 空间复杂度:\(O(1000)\)
  5. 桶排序/计数排序

    • 去重与排序一次性完成:利用数组下标天然有序的特性,标记出现过的数字,即可同时完成去重和排序。
    • 适用场景:当数据范围较小且需要去重排序时,桶排序是最简单高效的方法。

【算法标签】

普及- #其他排序

【代码详解】

#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
posted @ 2026-08-31 09:23  团爸讲算法  阅读(9)  评论(0)    收藏  举报