AcWing 3775 数组补全
给定一个的排列。
已知,对于始终成立。
现在,因为一些原因,数组中的部分元素丢失了。
请你将数组丢失的部分补全,要求数组在补全后仍然是一个的排列,并且对于均成立。
输入格式
第一行包含整数,表示共有组测试数据。
每组数据第一行包含一个整数。
第二行包含个整数。如果,则表示已经丢失,需要补全。
输出格式
每组数据一行,输出补全后的数组,整数之间空格隔开。
如果方案不唯一,则输出任意合理方案即可。
数据范围
,至少两个为。
同一测试点内所有的和不超过。
数据保证有解。
方法1
使用Set存下所有存在的数,再遍历一次找到Set中不存在的数,存入List中,这样就找到了元素中缺失的数字。将其从小到大排列,并将其按尾到头依次放到原数组中值为0的位置。这样就保证了最多只有一个数字满足(填入的数字是降序的,而数组序号是升序的)。再遍历一次填好后的数组,找到的位置,若这个位置是原数组缺失数字的第一个位置,那么将其与缺失数字的最后一个位置交换,否则就将其与第一个位置交换,这样就保证了数组中没有满足的数。
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-- > 0) {
int n = sc.nextInt();
int[] a = new int[n+1];
Set<Integer> set = new HashSet<>();
for(int i = 1;i <= n;i++) {
a[i] = sc.nextInt();
set.add(a[i]);
}
List<Integer> num = new ArrayList<>();
for(int i = 1;i <= n;i++) {
if(!set.contains(i)) {
num.add(i);
}
}
Collections.sort(num);
int l = 0,r = 0;
for(int i = 1,j = num.size() - 1;i <= n;i++) {
if(a[i] == 0) {
a[i] = num.get(j);
j--;
if(j == num.size() - 2) {
r = i;
}
if(j == -1) {
l = i;
}
}
}
for(int i = 1;i <= n;i++) {
if(a[i] == i) {
if(i == l) {
int temp = a[i];
a[i] = a[r];
a[r] = temp;
} else {
int temp = a[i];
a[i] = a[l];
a[l] = temp;
}
}
}
for(int i = 1;i <= n;i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
}
}
方法2:环图
使用两个数组分别存一个数i的下一个节点p[i]和上一个节点q[i],再用一个数组存下一个数是否被加入环中。当递推一个节点的往下的最后一个节点和往上的最后一个节点是同一个节点时,那么这是一个完整的环,此时就需要将其余的所有p[i]=0的节点存入一个新的环中;否则就将所有的孤立节点存入到这个不完整的环中,构成一个完整的环。
import java.util.*;
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
while(t-- > 0) {
int n = sc.nextInt();
int[] p = new int[n+10]; //p[i]=j表示i的下一个节点是j
int[] q = new int[n+10]; //p[i]=j表示i的上一个节点是j,即j节点指向i
boolean[] st = new boolean[n+10]; //每个节点是否已经加入到环图中
boolean flag = false;
for(int i = 1;i <= n;i++) {
p[i] = sc.nextInt();
q[p[i]] = i;
}
for(int i = 1;i <= n;i++) {
if(st[i] || p[i] == 0) { //i已经加入到环中,或者p[i]=0
continue;
}
st[i] = true;
int x = i,y = i;
while(p[x] != 0 && !st[p[x]]) { //将p[x]加入环中
x = p[x];
st[x] = true;
}
while(q[y] != 0 && !st[q[y]]) { //将q[y]加入环中
y = q[y];
st[y] = true;
}
if(p[x] == y) { //已经是一个完整的环
continue;
}
if(!flag) { //将所有孤立的节点(p[j]=0,q[j]=0)加入该环中,形成完整的环
flag = true;
for(int j = 1;j <= n;j++) {
if(p[j] == 0 && q[j] == 0) {
st[j] = true;
p[x] = j;
x = j;
}
}
}
p[x] = y;
}
if(!flag) { //将其余的节点(p[i]=0)的节点加入其它的环中
int x = 0,y = 0;
for(int i = 1;i <= n;i++) {
if(p[i] == 0) {
if(x == 0 && y == 0) {
x = i;
y = i;
} else {
p[x] = i;
x = i;
}
}
}
p[x] = y;
}
for(int i = 1;i <= n;i++) {
System.out.print(p[i] + " ");
}
System.out.println();
}
}
}