tarjan算法求解强连通分量

参考
基本概念:
强连通:有向图G中,如果某个结点v1有一条到v2的路径且v2有一条到v1的路径,则称这两个点强连通;
强连通图:若有向图G中任意两个点都强连通,则称G是强连通图;
强连通分量:有向图的极大强连通子图称为强连通分量,注意是极大而非最大,这包含了两个意思:1.有向图的强连通分量可以有多个;2.如果选取了某个子图G2且G2强连通,但在图G中还存在一个点v,使得v和G2中任意一个点强连通,那么G2不能称为强连通分量,只有当对于任意v∈G且v不属于G2,G2中存在一个点和v非强连通时,G2才能称为强连通分量;
tarjan算法基于dfs算法,利用栈的特性求解一个有向图的强连通分量,图中的顶点只会在一个强连通分量中出现,时间复杂度为O(V+E)。
其关键在于如何判断强连通分量的“根”,也就是在dfs的时候对于一个强连通分量首先访问到的结点。其实dfs的时候,假设当前点为u,那么会形成一棵以u为根的树;只是这棵树的某些节点和其父亲或祖先有联系;这个u就是我们所谓的“根”。
算法实现的过程中维护两个值,一个栈;
dfn[i]:代表i结点在dfs的时候的进入时间戳,也就是第几个被访问的
low[i]:作为点i在这颗树中的,最小的子树的根。。(懂不起)
栈:用于存储位于同一个强连通分量的结点;
实现过程:
1.选择一个未被dfs过的点开始dfs
2.给当前结点u的dfn[u]赋值,low[u]初始化为和dfn[u]相等;
3.把当前节点u压入栈中,并标记当前节点u处于栈中
4.遍历与当前的点u相连的点v
(1).如果v未被访问过,递归调用该算法,并在结束后更新low[u] = min(low[u],low[v]);
(2).如果v已被访问过且仍在栈中,直接更新low[u] = min(low[u],dfn[v]);
5.遍历结束后如果dfn[u] = low[u],则代表u是一个强连通分量的“根”,栈中位于u之后的点都和u处于同一个强连通分量;
举个例子:
字太丑了,自己都看不下去,。。。
有一说一,字太丑了,自己都看不下去,。。。
模板

void tarjan(int u,int p) {
    dfn[u] = low[u] = ++index;
    stk[top++] = u;
    vis[u] = 1;
    for(int i = head[u];~i; i = edge[i].nxt) {
        int v = edge[i].v;
        if(!dfn[v]) {
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
        }else if(vis[v]) {
            low[u] = min(low[u],dfn[v]);
        }
    }
    int tt;
    if(dfn[u] == low[u]) {
   		num++;  //用于保存强连通分量的个数
        do {
            tt = stk[--top];
            bel[tt] = num;
            vis[tt] = 0;
        }while(tt != u);
    }
}
//调用,如果原图不是联通图,那么就需要循环调用;
for(int i = 1;i <= n; ++i) {
            if(!dfn[i])  
                tarjan(i,0);
        }

由于强连通分量中各个点相互可达,有些时候可以当成一个点来处理,并且能够解决掉带环的问题,因为很多算法都需要无环这个条件;
例题:poj3160 (tarjan缩点+dp)

//#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<set>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 3e4 + 10;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
    int f=1,res=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
    return f*res;
}
struct EDG {
    int v,nxt;
}edge[150000 + 10];
vector<pair<int,int> >s[maxn];
int n,m,hpy[maxn],bel[maxn],stk[maxn],head[maxn],dfn[maxn],vis[maxn],low[maxn],cnt,index,top,num,w[maxn],dp[maxn];
void Initial() {
    cnt = index = top = 0,num = 1;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(dp,0,sizeof(dp));
    memset(w,0,sizeof(w));
}
void ade(int u,int v) {
    edge[cnt].v = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt++;
}
//tarjan缩点,从0开始存储
void tarjan(int u,int p) {
    dfn[u] = low[u] = ++index;
    stk[top++] = u;
    vis[u] = 1;
    for(int i = head[u];~i; i = edge[i].nxt) {
        int v = edge[i].v;
        if(!dfn[v]) {
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
        }else if(vis[v]) {
            low[u] = min(low[u],dfn[v]);
        }
    }
    int tt;
    if(dfn[u] == low[u]) {
        do {
            tt = stk[--top];
            bel[tt] = num;
            if(hpy[tt] > 0) w[num] += hpy[tt];
            vis[tt] = 0;
        }while(tt != u);
        num++;
    }
}
void mk_newmp() {
    for(int i = 0;i < num; ++i) s[i].clear();
    for(int i = 1;i <= n; ++i) {
        for(int j = head[i];~j; j = edge[j].nxt) {
            int v = edge[j].v;
            if(bel[i] != bel[v]) {
                s[bel[i]].push_back(make_pair(bel[v],w[bel[v]]));
            }
        }
    }
    for(int i = 1;i < num; ++i) {
        s[0].push_back(make_pair(i,w[i]));
    }
}
int dfs(int u) {
    if(dp[u] > 0) return dp[u];
    for(int i = 0;i < s[u].size();i++) {
        int v = s[u][i].first,val = s[u][i].second;
        dp[u] = max(dp[u],dfs(v) + val);
    }
    return dp[u];
}
int main()
{
    while(~scanf("%d%d",&n,&m)) {
        Initial();
        for(int i = 1;i <= n; ++i) hpy[i] = read();
        for(int i = 1;i <= m; ++i) {
            int u = read(),v = read();
            u++,v++;
            ade(u,v);
        }
        for(int i = 1;i <= n; ++i) {
            if(!dfn[i])
                tarjan(i,0);
        }
        mk_newmp();
        cout<<dfs(0)<<endl;
    }
    return 0;
}
发布了42 篇原创文章 · 获赞 29 · 访问量 7625

猜你喜欢

转载自blog.csdn.net/qq_44077455/article/details/104170269
今日推荐