数组补全(秋季每日一题 10)
原创
©著作权归作者所有:来自51CTO博客作者sweetheart7_7的原创作品,请联系作者获取转载授权,否则将追究法律责任
给定一个 的排列 。
已知,对于 ,
现在,因为一些原因,数组中的部分元素丢失了。
请你将数组丢失的部分补全,要求数组在补全后仍然是一个 的排列,并且对于 ,
输入格式
第一行包含整数 ,表示共有
每组数据第一行包含一个整数 。
第二行包含 个整数 。如果 ,则表示
输出格式
每组数据一行,输出补全后的
如果方案不唯一,则输出任意合理方案即可。
数据范围
至少两个 为 。
同一测试点内所有 的和不超过 。
数据保证有解。
输入样例:
3
5
5 0 0 2 4
7
7 0 0 1 4 0 6
7
7 4 0 3 0 5 1
输出样例:
5 3 1 2 4
7 3 2 1 4 5 6
7 4 2 3 6 5 1
- 记录孤立点
- 将孤立点插入任意一个开环中
- 将开环闭合
- 如何没有一个开环,则将孤立点集自成环
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N = 200010;
int n;
int pre[N], ne[N];
bool st[N]; // 标记孤立点
int main(){
int t;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
memset(ne, 0, 4*(n+1));
memset(pre, 0, 4*(n+1));
memset(st, 0, n+1); // 孤立点
for(int i = 1; i <= n; i++) scanf("%d", &ne[i]), pre[ne[i]] = i;
vector<int> v;
for(int i = 1; i <= n; i++)
if(!ne[i] && !pre[i]){
v.push_back(i);
st[i] = true;
}
bool flag = false;
for(int i = 1; i <= n; i++){
if(!st[i] && ne[i] && !pre[i]){ // 开环的头节点
int u = ne[i]; // 尾
while(ne[u]) u = ne[u];
if(!flag){
for(int j = 0; j < v.size(); j++){
ne[u] = v[j];
u = ne[u];
}
flag = true;
}
ne[u] = i; // 连成环
}
}
int hh = 0, tt = 0;
if(!flag){ // 如过孤立点没有被插入任何一个开环,则他们自己成环
for(int i = 0; i < v.size(); i++)
if(!hh && !tt) hh = tt = v[i];
else ne[tt] = v[i], tt = v[i];
ne[tt] = hh;
}
for(int i = 1; i <= n; i++) printf("%d ", ne[i]);
puts("");
}
return 0;
}