Tarjan算法
先是废话时间:说来挺惭愧 , 好几个月以前就学过tarjan算法然而现在才第一次写
tarjan缩点&dp
为啥要缩点答案显然
把环缩成一个点
然后图上拓扑dp
tarjan同名算法有很多 , 比如本blog的缩点与割点的tarjan算法其实并不是一个东西 , 但是很是相似
这个tarjan , 需要三个东西
第一:一个栈来存放搜到的点
第二:一个时间戳dfn , 表示第几个搜到这个点的
第三:low数组 , 表示够追溯到的最早的栈中节点的次序号
首先 , 初始化的时候肯定有
1 dfn[now] = low[now] = ++Index
然后开始遍历边
如果遍历到的点没有碰过
那么递归tarjan
递归结束后
1 low[now] = std::min(low[now], low[to]);
那么如果下一个点已经去过了呐
那么就不用递归了
1 low[now] = std::min(low[now], dfn[to]);
为什么是dfn呢?
照我的理解 , 应该是如果直接用low说不定会跳到这个环以外的地方
不过这个缩点tarjan里面把dfn换成low是可以的 , 但是一会的割点tarjan就不可以了
当最后时一个点的low == dfn , 那个这个点注定是一个缩点之后仍然存留在图上的一个点
那么就可以快落缩点了 , 把栈里的元素不停的向外弹就是了
然后根据题目性质拓扑dp , 没了
1 #include<cmath> 2 #include<queue> 3 #include<stack> 4 #include<cstdio> 5 #include<cstring> 6 #include<cstdlib> 7 #include<iostream> 8 #include<algorithm> 9 #define APART puts("----------------------") 10 #define debug 1 11 #define FILETEST 0 12 #define inf 100010 13 #define ll long long 14 #define ha 998244353 15 #define INF 0x7fffffff 16 #define INF_T 9223372036854775807 17 #define DEBUG printf("%s %d\n",__FUNCTION__,__LINE__) 18 19 namespace chino{ 20 21 inline void setting(){ 22 #if FILETEST 23 freopen("test.in", "r", stdin); 24 freopen("test.me.out", "w", stdout); 25 #endif 26 return; 27 } 28 29 inline int read(){ 30 char c = getchar(), up = c; int num = 0; 31 for(; c < ‘0‘ || c > ‘9‘; up = c, c = getchar()); 32 for(; c >= ‘0‘ && c <= ‘9‘; num = (num << 3) + (num << 1) + (c ^ ‘0‘), c = getchar()); 33 return up == ‘-‘ ? -num : num; 34 } 35 36 int n, m; 37 int ans; 38 int cntE, cntR, cntJ; 39 int val[inf], dp[inf]; 40 int vis[inf], in[inf]; 41 int belong[inf], dfn[inf], low[inf]; 42 int head[inf], rehead[inf]; 43 struct Edge{ 44 int to; 45 int from; 46 int next; 47 }e[inf << 1], r[inf << 1]; 48 std::queue <int> Q; 49 std::stack <int> S; 50 51 inline void AddEdge(int from, int to, int &cntE, int *head, Edge *e){ 52 ++cntE; 53 e[cntE].from = from; 54 e[cntE].to = to; 55 e[cntE].next = head[from]; 56 head[from] = cntE; 57 return; 58 } 59 60 void tarjan(int now){ 61 low[now] = dfn[now] = ++cntJ; 62 vis[now] = 1; 63 S.push(now); 64 for(int i = head[now]; i; i = e[i].next){ 65 int to = e[i].to; 66 if(dfn[to] == 0){ 67 tarjan(to); 68 low[now] = std::min(low[now], low[to]); 69 } else if(vis[to]) 70 low[now] = std::min(low[now], dfn[to]); 71 } 72 if(low[now] == dfn[now]){ 73 int top = S.top(); 74 while(1){ 75 top = S.top(); 76 S.pop(); 77 belong[top] = now; 78 vis[top] = 0; 79 if(top == now) 80 break; 81 val[now] += val[top]; 82 } 83 } 84 return; 85 } 86 87 inline void topoDP(){ 88 while(!Q.empty()) 89 Q.pop(); 90 for(int i = 1; i <= n; i++){ 91 if(belong[i] == i && in[i] == 0) 92 Q.push(i), dp[i] = val[i]; 93 } 94 while(!Q.empty()){ 95 int x = Q.front(); 96 Q.pop(); 97 for(int i = rehead[x]; i; i = r[i].next){ 98 int to = r[i].to; 99 dp[to] = std::max(dp[to], dp[x] + val[to]); 100 --in[to]; 101 if(in[to] == 0) 102 Q.push(to); 103 } 104 } 105 return; 106 } 107 108 inline int main(){ 109 n = read(); 110 m = read(); 111 for(int i = 1; i <= n; i++) 112 val[i] = read(); 113 for(int i = 1; i <= m; i++){ 114 int u = read(); 115 int v = read(); 116 AddEdge(u, v, cntE, head, e); 117 } 118 for(int i = 1; i <= n; i++){ 119 if(dfn[i] == 0) 120 tarjan(i); 121 } 122 for(int i = 1; i <= m; i++){ 123 int u = belong[e[i].from]; 124 int v = belong[e[i].to]; 125 if(u == v) 126 continue; 127 AddEdge(u, v, cntR, rehead, r); 128 ++in[v]; 129 } 130 topoDP(); 131 for(int i = 1; i <= n; i++) 132 ans = std::max(ans, dp[i]); 133 printf("%d\n", ans); 134 return 0; 135 } 136 137 }//namespace chino 138 139 int main(){return chino::main();}
复杂度O(n+m)
下一题:P3388 【模板】割点(割顶)
说在前面 , 这道题是无向边 , 上一题是有向边
而割点的定义是:
在无向连通图中,如果将其中一个点以及所有连接该点的边去掉,图就不再连通,那么这个点就叫做割点(cut vertex / articulation point)。
举例 , 这张图5点就是割点
求割点, 也是一个叫做tarjan的算法
首先
原文地址:https://www.cnblogs.com/chiarochinoful/p/algorithm-tarjan.html
时间: 2024-11-13 06:58:42