题解:学而思编程 独一无二
【题目来源】
学而思编程:独一无二
【题目描述】
X国的所有居民都具有一个共同的强迫症,那就是独特。对于任何东西他们都要尽可能保证独一无二,以至于独特成为了他们追求的完美。
他们的彩票中间规则也是十分独特,每一个买彩票的人选中一个范围在 \([1,10^4]\) 的数字,将所有数字按从小到大的顺序排成一排。数列中,只出现过一次且排位最靠前的那个数字就是中奖号码。
例如,\(7\) 个人购买了彩票,所买数字依次是\(5\) \(5\) \(17\) \(31\) \(67\) \(88\) \(88\),则第三个数字 \(17\) 为中奖号码。
【输入】
第一行一个整数 \(n\);
第二行 \(n\) 个空格隔开的整数表示彩票数字。
【输出】
一行一个整数,如果没有人中奖,则输出 \(-1\),反之输出中奖号码。
【输入样例】
7
5 5 17 31 67 88 88
【输出样例】
17
【核心思想】
-
问题分析:给定 \(n\) 个 \([1, 10^4]\) 范围内的整数,找出只出现过一次且数值最小的那个数。这是一个频次统计 + 最小值查找问题,关键在于先统计每个数字的出现次数,再按数值从小到大找到第一个出现次数为 \(1\) 的数。
-
算法选择:
- 计数数组:
num[x]记录数字 \(x\) 出现的次数 - 线性扫描找最小:遍历值域 \(1\) 到 \(100000\),第一个
num[i] == 1的 \(i\) 即为答案
- 计数数组:
-
关键步骤:
- 初始化:读取 \(n\)
- 统计频次:
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 读取 \(x\)
num[x]++
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 查找中奖号码:
- 遍历 \(i\) 从 \(1\) 到 \(100000\):
- 若
num[i] == 1:返回 \(i\)(第一个只出现一次的数,即数值最小且排位最靠前)
- 若
- 若遍历结束未找到:返回 \(-1\)
- 遍历 \(i\) 从 \(1\) 到 \(100000\):
- 输出结果
-
时间/空间复杂度:
- 时间复杂度:\(O(n + V)\),\(V = 10^5\) 为值域,统计 \(O(n)\),扫描 \(O(V)\)
- 空间复杂度:\(O(V)\),计数数组
num[1..V]
-
计数数组的核心思想:
- 频次统计:通过数组下标直接映射数字,\(O(1)\) 记录每次出现
- 最小值优先:按数值从小到大扫描,第一个满足条件的即为"排位最靠前"的,符合题目要求
- 值域利用:题目限定值域 \([1, 10^4]\),数组大小开到 \(10^5\) 足够,实现简单高效
- 无解处理:遍历完值域仍未找到
num[i] == 1,说明所有数都重复或没有数,返回 \(-1\) - 适用于"有限值域 + 频次约束 + 最小值查找"类问题,计数数组配合顺序扫描是标准解法
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, num[100010], x; // n: 数字个数, num: 计数数组, x: 临时变量
int work()
{
for (int i = 1; i <= 100000; i++) // 遍历1到100000
{
if (num[i] == 1) // 如果数字i只出现一次
{
return i; // 返回这个数字
}
}
return -1; // 没有找到只出现一次的数字
}
signed main()
{
cin >> n; // 输入数字个数
for (int i = 1; i <= n; i++) // 输入n个数字
{
cin >> x;
num[x]++; // 统计数字x出现的次数
}
cout << work() << endl; // 输出结果
return 0;
}
【运行结果】
7
5 5 17 31 67 88 88
17
浙公网安备 33010602011771号