谈谈Tarjan算法

Tarjan算法是一系列用dfs解决图论问题的算法,由美国计算机科学家Tarjan提出。

首先先分析有向图中Tarjan算法的应用:求有向图强连通分量。

首先介绍一些在Tarjan算法中特殊数组的定义,这个定义怎么提出来的咱也不知道,咱也不敢问。

一个是dfn数组,表示dfs搜索的时间戳,也就是第几个搜索到这个点。另一个是low数组,记录的是某个点经过若干条树边和至多一条非树边到达的最小的dfn。

再说说什么是非树边,树边。树边感性理解就是dfs会得到一棵dfs搜索树,然后这棵搜索树上的边就是树边,对应的不在树上的就是非树边。而非树边又分两种,返祖边和横叉边。好吧其实非树边具体是啥没用2333。

那么感性理解一下我们大致可以得到一个式子:low[u] = min(low[v],low[u]),u表示当前节点,v表示这个节点能到的节点。显然对于节点u,如果它的子节点v的low更小,换句话说v可以到达一个dfn更小的点,那么u同样可以经过u,v之间的树边到达那个dfn更小的点。

那如果某个点经过一顿操作,最后low值和dfn值仍相等,说明这个点到不了之前的点了,那这个点就是一个强连通分量的根。如果我们维护一个栈,把每个搜到的点放进去,如果搜完发现一个点low和dfn相等就把这个点以及栈里在它上面的点弹出去。因为这个点上面的点都是这个点可以到的点,都属于同一个强连通分量。

好的算法就结束了。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<stack>
  6 #include<queue>
  7 #define N 10010
  8 #define M 100010
  9 #define B printf("Break
");
 10 using namespace std;
 11 int head[N],nxt[M],to[M],val[N],in[N];
 12 int n,m,cnt;
 13 void add(int u,int v)
 14 {
 15     nxt[++cnt] = head[u];
 16     head[u] = cnt;
 17     to[cnt] = v;
 18     in[v]++;
 19     return;
 20 }
 21 void reset()
 22 {
 23     memset(head,0,sizeof(head));
 24     memset(nxt,0,sizeof(nxt));
 25     memset(to,0,sizeof(to));
 26     memset(in,0,sizeof(in));
 27     cnt = 0;
 28 }
 29 int dfn[N],low[N],tim;
 30 int scc[N];
 31 bool vis[N];
 32 stack<int>s;
 33 void tarjan(int u)
 34 {
 35     dfn[u] = low[u] = ++tim;
 36     vis[u] = 1;
 37     s.push(u);
 38     for(int i = head[u];i;i = nxt[i])
 39     {
 40         int v = to[i];
 41         if(!dfn[v])
 42         {
 43             tarjan(v);
 44             low[u] = min(low[u],low[v]);
 45         }
 46         else if(vis[v])
 47         {
 48             low[u] = min(low[u],dfn[v]);
 49         }
 50     }
 51     if(dfn[u] == low[u])
 52     {
 53         while(s.top() != u)
 54         {
 55             scc[s.top()] = u;
 56             vis[s.top()] = 0;
 57             val[u] += val[s.top()];
 58             s.pop();
 59         }
 60         s.pop();
 61         scc[u] = u;
 62         vis[u] = 0;
 63     }
 64 }
 65 int dis[N];
 66 int topo_sort()
 67 {
 68     queue<int>q;
 69     for(int i = 1;i <= n;i++)
 70     {
 71         if(scc[i] == i && !in[i])
 72         {
 73             q.push(i);
 74             dis[i] = val[i];
 75         }
 76     }
 77     while(q.size())
 78     {
 79         int u = q.front();
 80         q.pop();
 81         for(int i = head[u];i;i = nxt[i])
 82         {
 83             int v = to[i];
 84             dis[v] = max(dis[v],dis[u] + val[v]);
 85             in[v]--;
 86             if(!in[v]) q.push(v);
 87         }
 88     }
 89     int ans = 0;
 90     for(int i = 1;i <= n;i++) ans = max(ans,dis[i]);
 91     return ans;
 92 }
 93 int from[M],tto[M];
 94 int main()
 95 {
 96     scanf("%d %d",&n,&m);
 97     for(int i = 1;i <= n;i++)
 98     {
 99         scanf("%d",&val[i]);
100     }
101     for(int i = 1;i <= m;i++)
102     {
103         scanf("%d %d",&from[i],&tto[i]);
104         add(from[i],tto[i]);
105     }
106     for(int i = 1;i <= n;i++)
107     {
108         if(!dfn[i]) tarjan(i);
109     }
110     reset();
111     for(int i = 1;i <= m;i++)
112     {
113         int sf = scc[from[i]],st = scc[tto[i]];
114         if(sf != st)
115         {
116             add(sf,st);
117         }
118     }
119     printf("%d",topo_sort());
120 }
有向图缩点
原文地址:https://www.cnblogs.com/lijilai-oi/p/10809915.html