AcWing 3775. 数组补全
题意
给定一串少了数的数组,其中数为1~n,并且满足第 i 位上的数不为i;让你补全该数组;
输出任意一个满足条件的补全后的数组;
解题思路
可以把数组看成一个环,将下标为头,值为尾,将已知点连起来;
如图:
上面例子已将所有可以连的点相连,可以发现有一个未成环的串,和两个孤立点。构造过程:1位置上为7, 7位置上为6……直到6后面为0,然后找1前面为4,4前面为5,直到5前面没有;
这样我们只要把孤立点加到环里就好了;
还有一种情况就是除孤立点外已成环,那么就让孤立点自己形成环就好了;
例如:
代码如下
#include <iostream>
#include <cstring>
using namespace std;
const int N=200005;
bool st[N];//标记这个点有没有加入环
int p[N],q[N],n;//p存i下标下的值a,q存a值下的下标i;
int main(){
int T;
cin >> T;
while(T--){
cin >> n;
memset(q,0,sizeof q);
memset(st,0,sizeof st);
for(int i = 1; i <= n; i ++){
scanf("%d",&p[i]);
q[p[i]]=i;
}
bool flag=0;
for(int i = 1; i <= n; i ++){
if(st[i] || !p[i])
continue;
st[i]=1;
int x=i,y=i;
while(!st[p[x]] && p[x]){//找到环的头
x=p[x];
st[x]=1;
}
while(!st[q[y]] && q[y]){//找到环的尾
y=q[y];
st[y]=1;
}
if(p[x]==y)//如果头==尾,说明成了闭合环,则跳过,继续找下一个环
continue;
//没跳过说明找到了未成闭合环的串
if(!flag){//孤立点没被加过,将孤立点加进去
flag=1;//标记加过孤立点了
for(int j = 1; j <= n; j ++)
if(!p[j] && !q[j]){
st[j]=1;
p[x]=j;
x=j;
}
}
p[x]=y;//加完之后将环闭合
}
if(!flag){//如果孤立点还是没加过,说明环都是闭合的,将孤立点自己成环
int x=0,y=0;
for(int i = 1; i <= n; i ++){
if(!p[i]){
if(!x && !y) x=y=i;
else {
p[x]=i;
x=i;
}
}
}
p[x]=y;
}
for(int i=1;i<n;i++){
printf("%d ",p[i]);
}
cout << p[n] << endl;
}
return 0;
}