AcWing 数组补全 环图做法

数组补全

AcWing
来源: 3775.数组补全
在这里插入图片描述在这里插入图片描述这里难度给出是困难,但其实并不难,可能写的稍微麻烦一点,读懂的话很就很简单了
题意大概就是给出n个数,如果这个数为0,那么表示缺失,需要我们补一个数,最后将补好的数组输出
限制就是,这n个数是1-n中的数,并且第i个数不能为i
我这里的做法是转换成环图,将题意转换一下,其实就是构建环图,但是不可以有自环,就是自己指向自己的环,如果还是不太明白,那么看下面这张图,对于第一个样例
在这里插入图片描述
可以看出有一个缺口,第2个数没有连向下一个,第一个数也没有上一个数,第三个数既没有入也没有出,我们把这些点叫做流浪的点,可以想到,我们为了形成一个环,还要把所有的点用上,那就把3放到中间就可以了,2指向3,3指向1就构成了环

思路

遍历数组,对于每个点,找到头和尾,如果头和尾相连,那么直接跳过,如果没有,我们再找出所有流浪的点,加入到缺口中。对于后面所有还有缺口的环,直接头尾相连
注意点
1、st数组标记是否使用,因为每个点我们最多只需要动一次就行了
2、q数组表示上一个点,p数组表示小一点
3、如果没有缺口,那么流浪的点就要相互之间形成一个环
代码里有详细注释

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 2e5 + 10;  
int q[N], p[N];//下一个和上一个
int n, T;
bool st[N];

int main(){
    cin >> T;
    while(T --){
        memset(st, 0, sizeof st);//初始化标记
        memset(q, 0, sizeof q);//初始化上一个点
        cin >> n;
        for(int i = 1;i <= n;i++){
            cin >> p[i];//如果第i个数的下一个点
            q[p[i]] = i;//那么他的上一个点就是i
        }
        bool flag = false;//所有流浪的点都可以放入一个缺口中,只用放一次就行了
        for(int i = 1;i <= n;i++){
            if(st[i] || !p[i]) continue;
            //如果这个点处理过或者没有连接任何点,那就跳过
            //因为,如果你没有下一个点,那么这个点要么是一个尾巴,要么是一个流浪的点
            //如果是尾巴,我们一定可以通过别的数找到他,所以可以跳过
            st[i] = true;//先标记
            int x = i, y = i;//x表示尾,y表示头
            while(st[p[x]] == false && p[x]){//x往下找
                x = p[x];
                st[x] = true;//找到记得标记
            }
            while(st[q[y]] == false && q[y]){//y往上找
                y = q[y];
                st[y] = true;
            }
            if(p[x] == y) continue;//如果没有缺口直接跳过
            if(!flag){//如果有缺口并且我们还没有处理流浪的点
                flag = true;//表示处理过了,以后就不用处理了
                for(int j = 1;j <= n;j++){
                    if(!q[j] && !p[j]){//找到所以没有入也没有出的,那就是流浪的点
                        p[x] = j;//接到x的后面
                        x = j;
                        st[j] = true;//标记
                    }
                }
            }
            p[x] = y;//最后首尾相连封住缺口
        }
        if(!flag){
        //如果我们遍历了一遍,还没有把流浪的点处理,也就是说,没有存在缺口,都是完美的环
        //那么我们让流浪的点相互之间构成环
            int x = 0, y = 0;//初始化头和尾是0
            for(int i = 1;i <= n;i++){
                if(!p[i]){
                    if(!x && !y) x = i, y = i;//找到第一个流浪的点作为头和尾
                    else{
                        p[x] = i;//从第二个开始往下找
                        x = i;
                    }
                }
            }
            p[x] = y;//最后封个口
        }
        for(int i = 1;i <= n;i++){//输出p数组即可
            if(i != 1) cout << ' ';//调整格式,不调也没事,现在评测器会忽略
            cout << p[i];
        }
        cout << endl;
    }
    return 0;
}

如果不想用环图用其他方法也可以做出来,只是我觉得这样做思路还算清晰,还有就是,y总yyds

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Miss .

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值