0.题目
时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
给定一个长度为 N 的数组 A = [A1, A2, · · · AN],数组中有可能有重复出现的整数。
现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改
A2, A3, · · · , AN。
当修改 Ai 时,小明会检查 Ai 是否在 A1 ~ Ai-1 中出现过。如果出现过,则小明会给 Ai 加上 1 ;如果新的 Ai 仍在之前出现过,小明会持续给 Ai 加 1 ,直到 Ai 没有在 A1 ~ Ai-1 中出现过。
当 AN 也经过上述修改之后,显然 A 数组中就没有重复的整数了。现在给定初始的 A 数组,请你计算出最终的 A 数组。
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数 A1, A2, · · · , AN 。
【输出格式】
输出 N 个整数,依次是最终的 A1, A2, · · · , AN。
【样例输入】
5
2 1 1 3 4
【样例输出】
2 1 3 4 5
【评测用例规模与约定】
对于 80% 的评测用例,1 ≤ N ≤ 10000。
对于所有评测用例,1 ≤ N ≤ 100000,1 ≤ Ai ≤ 1000000。
1.题解
1.1 暴力枚举
思路
思路非常好想,使用一个bool数组标记当前位是否被选过
两层循环(一层for,一层while),外层for用于遍历输入的数, while用于从头开始判断标记位,是则停止,标记;否则不断将当前数++;
但是总体耗时 O(n^2), 很明显会超时!
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1000010;
int n;
int a[N];
bool st[N];
int main()
{
cin >> n;
for (int i = 0; i < n; i++) scanf("%d", &a[i]);
st[a[0]] = true;
for (int i = 1; i < n; i ++)
{
if (!st[a[i]]) st[a[i]] = true;
else
{
while (st[a[i]]) a[i] ++;
st[a[i]] = true;
}
}
for (int i = 0; i < n; i++) printf("%d ", a[i]);
return 0;
}
1.2 并查集
思路
这题的难点主要是能否想到用并查集来解决,我们想要知道我们每次选到某个数,究竟是输出其本身还是其他数?
我们模拟一下思路,如果我们选择1, 那我们首先去找1是否被选择,如果被选择了,然后再寻找2是否被选择....最后找到那个未被选择的数输出.但这样总体时间复杂度高达O(n^2),并不是我们想要的; 我们想要知道我们如果选择1, 如果重复,立即能知道我们应该选择的下一个数.
如果我们修改标记位由bool记录是否重复 -> 记录下一个位置呢?
比如像我们第一次选择了1, 那么flag[1] = 1 + 1 = 2, 再次选择就是 flag[1] = 2 + 1 = 3呢?
这里又有一个问题了,我们维护的难题, 像这里应该同时更新 flag[2] = 2 + 1 = 3; 也就是说我们要同时所有更新值为 flag[1] 的数均 + 1; 这样维护的成本又变成O(n^2)
如何既要更新记录值,又能很好的维护标记呢?
我们想到一种很有用的结构:树以及对应的结构--并查集
我们可以将每个数视为一个节点,节点的值表示该数。我们初始化时,每个数的记录值为它的下一个数,即记录值为当前数加一。
然后,我们通过 find 操作来寻找当前数的记录值。如果当前数的记录值等于它本身,说明它还没有被选择过,直接返回当前数;否则,我们递归地调用 find 操作,找到记录值对应的下一个数,直到找到一个未被选择的数为止(这里如果使用路径压缩,每次find操作会及时维护更新并查集,使树的高度始终维持在较小的范围内,可以默认寻找效率为O(1))。最后,我们将当前数的记录值更新为下一个未被选择的数,以便下一次选择数时能够快速找到。
这样,每次选择数时,我们只需通过 find 操作来找到下一个未被选择的数,时间复杂度为 O(1),非常高效
代码
#include<bits/stdc++.h>
using namespace std;
const int Maxn = 1e6 + 10;
int fa[Maxn + 1];
void init(){
for(int i = 1; i <= Maxn; i++){
fa[i] = i;
}
}
// 压缩路径
int find(int x){
if(x == fa[x]){
return x;
} else{
fa[x] = find(fa[x]);
return fa[x];
}
}
// 根节点合并
void merge(int x, int y){
fa[find(x)] = find(y);
}
int main(){
int n;
cin >> n;
init();
for(int i = 0; i < n; i++){
int a;
cin >> a;
int t = find(a); // 下一个未被选择的数
cout << t << " ";
merge(t, t+1); // 下一个未被选择的数(根节点)更新为 find(t+1)
}
return 0;
}
标签:选择,int,查集,++,st,蓝桥,Ai,2019,数组
From: https://www.cnblogs.com/trmbh12/p/18132015