对于又要排序又要去重的基础题。比如
P1059 [NOIP 2006 普及组] 明明的随机数
题目描述
明明想在学校中请一些同学一起做一项问卷调查,为了实验的客观性,他先用计算机生成了 NNN 个 111 到 100010001000 之间的随机整数 (N≤100)(N\\leq100)(N≤100),对于其中重复的数字,只保留一个,把其余相同的数去掉,不同的数对应着不同的学生的学号。然后再把这些数从小到大排序,按照排好的顺序去找同学做调查。请你协助明明完成“去重”与“排序”的工作。
输入格式
输入有两行,第 111 行为 111 个正整数,表示所生成的随机数的个数 NNN。
第 222 行有 NNN 个用空格隔开的正整数,为所产生的随机数。
输出格式
输出也是两行,第 111 行为 111 个正整数 MMM,表示不相同的随机数的个数。
第 222 行为 MMM 个用空格隔开的正整数,为从小到大排好序的不相同的随机数。
输入输出样例 #1
输入 #1
10
20 40 32 67 40 20 89 300 400 15
输出 #1
8
15 20 32 40 67 89 300 400
说明/提示
NOIP 2006 普及组 第一题
这道题不难,代码也很短,但本人写起来觉得它很烦。为啥呢?因为用 sort 的话去重很烦(unique太难拼了,没学过的忽略这一句),手动排序——呃,谁学了 sort 之后还用手动排啊。
于是就这样,这道题很烦,归根结底,原因还是在于太老掉牙了(这种题没做过十次也有八次了)于是我今天分享一个新奇的方法。
首先,众所周知,c++里有一个STL容器叫 set(集合)。
以下是它的自带函数:
| s.insert(val) | 插入元素 val;重复元素直接忽略 |
| s.size() | 返回集合中元素个数(去重后的数量) |
| s.empty() | 集合为空返回 true,否则 false |
| s.clear() | 清空所有元素 |
| s.find(val) | 查找 val,返回迭代器;找到→指向该元素;找不到→s.end() |
| s.erase(val) | 删除值为 val 的所有元素 |
| s.erase(迭代器) | 删除迭代器指向的单个元素 |
| s.begin() | 迭代器,指向最小元素(第一个) |
| s.end() | 尾后迭代器,不指向有效元素,遍历终止条件 |
核心特性
自动有序:
容器内部使用红黑树(平衡二叉搜索树)存储元素,默认从小到大升序排列
元素唯一(自动去重):
不能存
放重复值;
插入相同元素不会报错,但是插入无效
不支持随机访问:
不能用 s[0]、s[1] 下标取值,只能依靠迭代器遍历
迭代器双向遍历,只能 ++it、–it
简单来说就是这个东西可以自动排序去重,简直就是专门为这道题设计的。所以我们要做的就是: 输入 -> 输出。
即
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
set<int> s;
for (int i = 1; i <= n; ++i){
int x;
cin >> x;
s.insert(x);
}
cout << s.size() << endl;
for (auto it = s.begin(); it != s.end(); ++it)
cout << *it<<" ";
cout << endl;
return 0;
}
for (auto it = s.begin(); it != s.end(); ++it) 这个是用迭代器遍历,没学过的就把这一句背下来并知道 set 只能用这个遍历就行了。(本文不负责讲解迭代器,若想详细学习,见《C++ STL迭代器完全指南:从原理到实战》)
所以我们用这段代码就能过这道题。是不是挺简便的。
本文到这里就差不多要结束了,多谢浏览。





