zoukankan      html  css  js  c++  java
  • Codeforces Gym 100851 K King's Inspection ( 哈密顿回路 && 模拟 )

    题目链接

    题意 : 给出 N 个点(最多 1e6 )和 M 条边 (最多 N + 20 条 )要你输出一条从 1 开始回到 1 的哈密顿回路路径,不存在则输出 " There is no route, Karl! "

    分析 : 

    题意很简单明了

    众所周知,哈密顿回路是个 NP-Hard 问题

    这么多个点的哈密顿回路肯定是不能暴力去寻找的

    但是关注到 M ≤ N + 20 这个特殊的条件

    那就说明图中肯定有很多单向链

    那么这题就很明确了,就把所有的单链缩成一个点

    然后再去 DFS 暴力找

    口胡起来貌似很简单,写起来是真的 _(´ཀ`」 ∠)_ 

    写了我挺久的,主要是Debug了挺久

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 2e5 + 10;
    struct EDGE{ int v, nxt; }Edge[maxn<<1];
    int Chain[maxn][2];///记录链的出度和入度
    int N, M, Head[maxn], cnt;
    int id, st;///分配ID的计数变量、寻找哈密顿回路的起点
    int IN[maxn], OUT[maxn];///每个点的入度和出度数量,方便判断一个节点是否为单链的节点
    int ID[maxn], vtx[maxn];///每个节点重新分配的ID、每个ID对应的节点or链
    bool isChain[maxn], vis[maxn];///判断是否是链、DFS中的判重数组
    int c[maxn];///并查集数组,判断图是否弱连通
    vector<int> arr;///存储最终答案
    
    
    int Findset(int x)
    {
        int root = x;
        while(c[root] != root)
            root = c[root];
    
        int idx;
        while(c[x] != root){
            idx = c[x];
            c[x] = root;
            x = idx;
        }
        return root;
    }
    
    inline void Join(int a, int b)
    {
        int A = Findset(a);
        int B = Findset(b);
        if(A != B) c[A] = B;
    }
    
    inline void init()
    {
        for(int i=0; i<maxn; i++) c[i] = i;
        memset(ID, -1, sizeof(ID));
        memset(vtx, -1, sizeof(vtx));
        memset(Head, -1, sizeof(Head));
        memset(isChain, false, sizeof(isChain));
        cnt = id = 0;
        st = -1;
    }
    
    inline void AddEdge(int From, int To)
    {
        Edge[cnt].v = To;
        Edge[cnt].nxt = Head[From];
        Head[From] = cnt++;
    }
    
    inline void ColorChain(int v, int Eiv, int Eid)
    {
        Chain[id][0] = v;
        bool loop = false;///链有可能构成环、如果是环,则出入度是一样的,标记一下
    
        while(!(IN[Eiv] != 1 || OUT[Eiv] != 1)){
            if(ID[Eiv] == -1) { ID[Eiv] = id; if(vtx[id] == -1) vtx[id] = Eiv; }
            else { loop = true; break; }
    
            isChain[Eiv] = true;
    
            Eiv = Edge[Eid].v;
            Eid = Head[Eiv];
        }
    
        Chain[id][1] = loop ? v : Eiv;
    
        id++;
    }
    
    inline void Color(int v)
    {
        if(IN[v] == 1 && OUT[v] == 1) return;///如果是单链上的节点就跳过,因为我的染色找链
                                             ///总是去找链的入度节点才去为这个链分配ID
    
        Chain[id][0] = v, Chain[id][1] = -1;///可以无视这个,没啥用
        ID[v] = id++;
        vtx[id-1] = v;
        int Eid, Eiv;
    
        for(int i=Head[v]; i!=-1; i=Edge[i].nxt){
            Eiv = Edge[i].v;
            Eid = i;
            if(IN[Eiv] == 1 && OUT[Eiv] == 1)///说明v是一个链的入度
                ColorChain(v, Eiv, Head[Eiv]);///为链染色
        }
    }
    
    int ans[maxn];
    bool DFS(int v, int num)///寻找哈密顿回路,分是否是链的两种情况
    {
        if(num == id && v == st) return true;
        if(vis[ID[v]]) return false;
    
        vis[ID[v]] = true;
        ans[num] = ID[v];
    
        if(isChain[v]){
            if( DFS(Chain[ID[v]][1], num+1) )///如果是链的开头,直接递归进其出度
                return true;
        }else{
            for(int i=Head[v]; i!=-1; i=Edge[i].nxt)///如果不是链则正常访问其所有出度
                if( DFS(Edge[i].v, num+1) )
                    return true;;
        }
    
        vis[ID[v]] = false;
        return false;
    }
    
    inline void Print_Chain(int v, int Chain_id)
    {
        while(IN[v]==1 && OUT[v]==1){
            arr.push_back(v);
            v = Edge[Head[v]].v;
        }
    }
    
    int main(void)
    {
        freopen("king.in", "r", stdin);///提交记得加这两个 freopen
        freopen("king.out", "w", stdout);
        scanf("%d %d", &N, &M);
    
        init();
    
        int From, To;
        while(M--){
            scanf("%d %d", &From, &To);
            AddEdge(From, To);
            Join(From, To);
            IN[To]++, OUT[From]++;
        }
    
        int root = Findset(1);
        for(int i=2; i<=N; i++){
            if(Findset(i) != root){
                puts("There is no route, Karl!");
                return 0;
            }
        }
    
        for(int i=1; i<=N; i++){
            if(st == -1 && (IN[i] != 1 || OUT[i] != 1)) st = i;
            if(ID[i] != -1) continue;
            else Color(i);///给每一个点配上一个ID,如果是链,则链上顶点ID一致,
                          ///且当前节点 isChain = true 标记是链
        }
    
        if(id == 0){
            int v = 1;
            bool flag = true;
            while(flag || v != 1){
                flag = false;
                printf("%d ", v);
                v = Edge[Head[v]].v;
            }puts("1");
            return 0;
        }
    
    
        memset(vis, false, sizeof(vis));
        if(DFS(st, 0)){
            for(int i=0; i<id; i++){
               int cur = vtx[ans[i]];
               if(isChain[cur]) Print_Chain(cur, ans[i]);
               else arr.push_back(cur);
            }
    
            bool BEGIN = false;
            int Count = 0;
            int len = arr.size();
    
            for(int i=0; i<len && Count<len; i=(i+1)%len){///因为不保证总是从1开始dfs,所以要去调整一下输出
                if(arr[i] == 1) BEGIN = true;
                if(BEGIN){
                    printf("%d ", arr[i]);
                    Count++;
                }
            }puts("1");
    
        }else puts("There is no route, Karl!");
        return 0;
    }
    /*
    10 11
    10 8
    5 4
    9 1
    4 3
    7 2
    2 6
    6 5
    8 9
    1 7
    10 3
    3 10
    */
    View Code
  • 相关阅读:
    Selenium(Python)等待元素出现
    java文件的I/O
    Map的四种遍历方式
    模板类实现链表
    字符串相关库函数使用
    动态规划之背包问题
    最长递增子序列
    深度优先搜索(DFS),逃离迷宫
    素数环问题(递归回溯)
    枚举(百鸡问题)
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/8818982.html
Copyright © 2011-2022 走看看