zoukankan      html  css  js  c++  java
  • AcWing 3775. 数组补全(环图)

    每日一题是不可能做出来的
    参考:https://www.acwing.com/video/3319/

    题目

    给定一个 1∼n 的排列 f1,f2,…,fn。
    已知,对于 1≤i≤n,fi≠i 始终成立。
    现在,因为一些原因,数组中的部分元素丢失了。
    请你将数组丢失的部分补全,要求数组在补全后仍然是一个 1∼n 的排列,并且对于 1≤i≤n, fi≠i 均成立。

    输入输出

    输入:
    第一行包含整数 T,表示共有 T 组测试数据。
    每组数据第一行包含一个整数 n。
    第二行包含 n 个整数 f1,f2,…,fn。如果 fi=0,则表示 fi 已经丢失,需要补全。
    输出:
    每组数据一行,输出补全后的 f 数组,整数之间空格隔开。
    如果方案不唯一,则输出任意合理方案即可。

    思路

    i只出现一次,fi也只出现一次,所以一定能构成环。(离散数学里叫圈)
    将所有不缺失的元素构成环(可能有多个),i->fi构成一条有向边,不存在自环。
    将缺失的元素填入任意一个环的缺口中,使环封闭,将其他的环也封闭。如果不存在有缺口的环,就将缺失的元素自己连成一个环。
    代码实现
    从头到尾遍历每个点,找到每个点所在环的头结点和尾结点,将缺失的元素加到头结点和尾结点之间(该操作只需要进行一次),将其他环头结点和尾结点相连。
    需要三个数组出边p[N],入边q[N],st[N]记录当前点是否被访问过
    根据思路自己写一遍

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    const int N = 200010;
    int p[N],q[N];
    bool st[N];
    
    int main()
    {
        int T;
        cin >> T;
        while (T -- ){
            memset(q, 0, sizeof q);
            memset(st, 0, sizeof st);
            int n;
            cin >> n;
            for (int i = 1; i <= n; i ++ ){
                cin >> p[i];    //输入i的出边
                q[p[i]] = i;    //记录p[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;   //寻找i所在环的头结点和尾结点
                while(!st[p[x]] && p[x]){  //寻找i的头结点
                    x = p[x];
                    st[x] = true;   //访问当前点
                }  
                while(!st[q[y]] && q[y]){   //寻找x的尾结点
                    y = q[y];
                    st[y] = true;
                }
                if(p[x] == y) continue; //当前i所在的环没有缺口了
                if(!flag){  //缺失的点还没有被操作过
                    flag = true;    //更新flag标记
                    for (int j = 1; j <= n; j ++ ){ //寻找孤立点
                        if(!p[j] && !q[j]){
                            st[j] = true;   //不要忘了更新这个点的状态
                            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 ++ ){ //输出方案
                cout << p[i] << " ";
            }
            cout << endl;
        }
        return 0;
    }
    
    作者:inss!w!
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 相关阅读:
    CentOS 下安装apt-get
    Centos 6.5升级到Git2.1.2的步骤
    JAVA常识积累
    Android网络编程概述
    解读ClassLoader
    android ndk调用OpenGL 实现纹理贴图Texture
    android Bind机制(二)
    Android Binder机制原理(史上最强理解,没有之一)(转)
    NDK常见问题
    根因分析初探:一种报警聚类算法在业务系统的落地实施
  • 原文地址:https://www.cnblogs.com/Hfolsvh/p/15041721.html
Copyright © 2011-2022 走看看