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 }