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\n", 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;
}
  

 

posted @ 2017-09-13 14:31  DearDongchen  阅读(247)  评论(0编辑  收藏  举报