• tarjan算法


    tarjan用来求有向图上强连通分量

    强连通分量就是图的一个子图,这个子图上任意两点都可以相互到达

    tarjan是基于dfs的算法,从一个点开始探索,dfn数组存每点探索的时间戳,low数组存每点下面能找到的最小的时间戳

    显然当一个点dfn==low时 这个点是一个强连通分量的根

    虽然是dfs不过没有回溯,每个点每个边只访问一次 复杂度O(n+m)

    https://nanti.jisuanke.com/t/16955

    前几天乌鲁木齐网络赛

    给一个有向图问最少添加几条边可以让图强连通

    其实每个强连通分量可以看成一个点(反正这里面每两个点可达)

    要整个图连通, 每个“点”都要有入度和出度,这时只需要遍历每条边

    如果边u->v连接了两个不同的分量,那就out[flag[u]]++,in[flag[v]]++,表示这两个联通分量一个有出度一个有入度

    u->v连接相同分量不用管

    最后对每个连通分量统计一下缺的出度入度,至少要补完这些出度入度,所以输出大的值

    #include<cstdio>
    #include<stack>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn = 1e4+7, maxm = 1e5+7;
    struct edge{
        int v, nxt;
        edge(){}
        edge(int v, int nxt):v(v), nxt(nxt){}
    }e[maxm];
    int head[maxn], ins[maxn], dfn[maxn], low[maxn], flag[maxn], cur, Index, cnt;
    int in[maxn], out[maxn], n, m;
    stack<int>S;
    void addedge(int u, int v){
        e[cur] = edge(v, head[u]);
        head[u] = cur++;
    }
    void tarjan(int u){
        dfn[u] = low[u] = Index++;
        ins[u] = 1;
        S.push(u);
        for(int i = head[u]; ~i; i = e[i].nxt){
            int v = e[i].v;
            if(dfn[v] == -1){
                tarjan(v);
                low[u] = min(low[u], low[v]);
            }
            else if(ins[v]){
                low[u] = min(low[u], dfn[v]);
            }
        }
        if(low[u] == dfn[u]){
            cnt++;
            while(1){
                int tmp = S.top();
                S.pop();
                ins[tmp] = 0;
                flag[tmp] = cnt;
                if(tmp == u)
                    break;
            }
        }
    }
    void init(){
        memset(head, -1, sizeof(head));
        memset(ins, 0, sizeof(ins));
        memset(dfn, -1, sizeof(dfn));
        memset(low, -1, sizeof(low));
        memset(flag, 0, sizeof(flag));
        memset(in, 0, sizeof(in));
        memset(out, 0, sizeof(out));
        cur = Index = cnt = 0;
    }
    void work(){
        for(int i = 1; i <= n; i++){
            if(dfn[i] == -1)
                tarjan(i);
        }
        for(int i = 1; i <= n; i++){
            for(int j = head[i]; ~j; j = e[j].nxt){
                int v = e[j].v;
                if(flag[i] != flag[v]){
                    out[flag[i]]++;
                    in[flag[v]]++;
                }
            }
        }
        int lossin = 0, lossout = 0;
        for(int i = 1; i <= cnt; i++){
            if(!in[i])
                lossin++;
            if(!out[i])
                lossout++;
        }
        if(cnt == 1){
            puts("0");
        }
        else{
            printf("%d
    ", max(lossin, lossout));
        }
    }
    int main(){
        int t;
        scanf("%d", &t);
        while(t--){
            init();
            scanf("%d%d", &n, &m);
            while(m--){
                int u, v;
                scanf("%d%d", &u, &v);
                addedge(u, v);
            }
            work();
        }
        return 0;
    }
      
    搞图论是没有用的,转行做数学题了hh
  • 相关阅读:
    【洛谷4251】 [SCOI2015]小凸玩矩阵(二分答案,二分图匹配)
    JXOI2019游记
    luogu4884 多少个1?
    数论难点选讲
    计树问题小结
    codeforces选做1.0
    POI2015选做
    后缀自动机小结
    bzoj4008 [HNOI2015]亚瑟王
    bzoj1500 [NOI2005]维修数列
  • 原文地址:https://www.cnblogs.com/DearDongchen/p/7514781.html
Copyright © 2020-2023  润新知