初涉tarjan缩点
Posted antiquality
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初涉tarjan缩点相关的知识,希望对你有一定的参考价值。
tarjan缩点:口胡过好多题,不过从来没写过……
什么是缩点
tarjan和Kosaraju、Gabow算法一样,是为了求有向图中的强连通分量。因为有向图中大多数情况下会有环存在,而有环是一个不甚好的性质。如果把有向图里的所有强连通分量都看作是一个点(缩点),则原图就会变成一个DAG——DAG是一个好东西。
什么是tarjan缩点
tarjan算法网上大多有介绍,我也在之前看过多次,不过从未写过,这里不再介绍。
今天把核心代码重新看了一遍,终于深入理解了其算法。那么就不妨在这里直接放上代码。
tarjan代码
1 void tarjan(int now) 2 { 3 dfn[now] = low[now] = ++tim; //常规的dfn[]和low[] 4 stk[++cnt] = now; 5 for (int i=head[now]; i!=-1; i=nxt[i]) 6 { 7 int v = edges[i]; 8 if (!dfn[v]){ 9 tarjan(v); 10 low[now] = std::min(low[now], low[v]); 11 }else if (!col[v]) 12 low[now] = std::min(low[now], dfn[v]); //注意这里是dfn[v] 13 } 14 if (low[now]==dfn[now]) //最后的统计部分 15 { 16 col[now] = ++cols; 17 for (; stk[cnt]!=now; cnt--) 18 col[stk[cnt]] = cols; 19 cnt--; 20 } 21 }
用途
1.有向图的缩点
2.解决2-SAT
几道例题
P3387 【模板】缩点
题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
说明
n<=10^4,m<=10^5,点权<=1000
算法:Tarjan缩点+DAGdp
题目分析
嘛……具体算法在题面里都给出来了。缩点+dp。
我做法是缩完点后重新建边,长是长了点,不过要是到了其他题的话可移植性会大一些。
至于dp,只需要建一个超级源,然后从超级源开始记忆化搜索就好了。
1 #include<bits/stdc++.h> 2 const int maxn = 10005; 3 const int maxm = 100035; 4 5 int n,m; 6 int f[maxn],val[maxn],col[maxn],cols; 7 int edgeTot,edges[maxm],nxt[maxm],head[maxn]; 8 bool vis[maxn]; 9 10 int read() 11 { 12 char ch = getchar(); 13 int num = 0; 14 bool fl = 0; 15 for (; !isdigit(ch); ch = getchar()) 16 if (ch==‘-‘) fl = 1; 17 for (; isdigit(ch); ch = getchar()) 18 num = (num<<1)+(num<<3)+ch-48; 19 if (fl) num = -num; 20 return num; 21 } 22 namespace tarjanSpace 23 { 24 int stk[maxn],cnt; 25 int a[maxn],dfn[maxn],low[maxn],tim; 26 int edgeTot,edges[maxm],nxt[maxm],head[maxn]; 27 void tarjan(int now) 28 { 29 dfn[now] = low[now] = ++tim; 30 stk[++cnt] = now; 31 for (int i=head[now]; i!=-1; i=nxt[i]) 32 { 33 int v = edges[i]; 34 if (!dfn[v]){ 35 tarjan(v); 36 low[now] = std::min(low[now], low[v]); 37 }else if (!col[v]) 38 low[now] = std::min(low[now], dfn[v]); 39 } 40 if (low[now]==dfn[now]) 41 { 42 ::col[now] = ++::cols; 43 for (; stk[cnt]!=now; cnt--) 44 ::col[stk[cnt]] = ::cols; 45 cnt--; 46 } 47 } 48 inline void addedgeInner(int u, int v) 49 { 50 edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot; 51 } 52 inline void addedgeOuter(int u, int v) 53 { 54 ::edges[++::edgeTot] = v, ::nxt[::edgeTot] = ::head[u], ::head[u] = ::edgeTot; 55 } 56 void dealOuter() 57 { 58 for (int i=1; i<=n; i++) ::val[::col[i]] += a[i]; 59 for (int i=1; i<=n; i++) 60 { 61 int u = col[i]; 62 for (int j=head[i]; j!=-1; j=nxt[j]) 63 { 64 int v = col[edges[j]]; 65 addedgeOuter(u, v); 66 } 67 } 68 for (int i=1; i<cols; i++) addedgeOuter(0, i); 69 } 70 void solve() 71 { 72 memset(head, -1, sizeof head); 73 n = read(), m = read(), cnt = tim = edgeTot = 0; 74 for (int i=1; i<=n; i++) a[i] = read(), addedgeInner(0, i); 75 for (int i=1; i<=m; i++) 76 { 77 int u = read(), v = read(); 78 addedgeInner(u, v); 79 } 80 tarjan(0); 81 dealOuter(); 82 } 83 } 84 void dp(int now) 85 { 86 if (vis[now]) return; 87 vis[now] = 1; 88 for (int i=head[now]; i!=-1; i=nxt[i]) 89 { 90 int v = edges[i]; 91 dp(v); 92 f[now] = std::max(f[now], f[v]); 93 } 94 f[now] += val[now]; 95 } 96 int main() 97 { 98 memset(head, -1, sizeof head); 99 tarjanSpace::solve(); 100 dp(0); 101 printf("%d ",f[0]); 102 return 0; 103 }
END
以上是关于初涉tarjan缩点的主要内容,如果未能解决你的问题,请参考以下文章
洛谷 P2194 HXY烧情侣Tarjan缩点 分析+题解代码