Tarjan算法初探(2):缩点
接上一节 Tarjan算法初探(1):Tarjan如何求有向图的强连通分量
Tarjan算法一个非常重要的应用就是 在一张题目性质在点上性质能够合并的普通有向图中将整个强连通分量视作一个点来把整张图变成一张DAG(即有向无环图) 而DAG的形态满足最优子结构经常与DP联系在一起 故缩点常作为一条桥梁将图论与DP相联系
缩点思想不难理解 这里主要说明一下代码的操作细节与流程:
1.使用Tarjan算法求出每个点属于哪一个强连通分量
2.枚举每一条点将每一个点对应性质合并到新的点上
3.枚举每一条边将每条边两端的点对应的新点相连来重构整张图(注意自环的问题) 重构时可能有的重边不影响重构图是一个DAG
其中 2 3 步可同时进行。
下面贴一道模板题来熟悉代码
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。 n<=10^4,m<=10^5,0<=点权<=1000
分析:
裸题 缩点以后直接DP完事 没有思维难度的DP
1 #include <cstdio> 2 #include <iostream> 3 #define re register 4 #define r(x) x=read() 5 #define MAXX 10005 6 #define MIN(a,b) (a<b?a:b) 7 #define MAX(a,b) (a>b?a:b) 8 using namespace std; 9 int dfn[MAXX],low[MAXX],que[MAXX],ans[MAXX],w[MAXX],w2[MAXX],id[MAXX], 10 st[MAXX],top,u,to,n,m,cnt[2],k,num,h[2][MAXX],sum[MAXX]; 11 struct edge{int to,nex;}e[2][MAXX<<3]; 12 void add(int u,int to,int id) 13 { 14 ++cnt[id]; 15 e[id][cnt[id]]=(edge){to,h[id][u]}; 16 h[id][u]=cnt[id]; 17 } 18 int read() 19 { 20 char ch=0;int w=0,ff=1; 21 while(ch<'0'||ch>'9'){if(ch=='-')ff=-1;ch=getchar();} 22 while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();} 23 return ff*w; 24 } 25 void tarjan(int now)//求强连通分量 26 { 27 low[now]=dfn[now]=++k; 28 st[++top]=now; 29 for(int i=h[0][now];i;i=e[0][i].nex) 30 { 31 if(!dfn[e[0][i].to]) 32 tarjan(e[0][i].to),low[now]=MIN(low[now],low[e[0][i].to]); 33 else 34 if(!id[e[0][i].to]) 35 low[now]=MIN(low[now],dfn[e[0][i].to]); 36 } 37 if(low[now]==dfn[now]) 38 { 39 id[now]=++num; 40 while(st[top]!=now) 41 { 42 id[st[top]]=num; 43 --top; 44 } 45 --top; 46 } 47 } 48 void build()//缩点 49 { 50 for(int i=1;i<=n;++i) 51 { 52 w2[id[i]]+=w[i];//点权值合并 53 for(int j=h[0][i];j;j=e[0][j].nex) 54 if(id[i]!=id[e[0][j].to]) 55 add(id[i],id[e[0][j].to],1),++sum[id[e[0][j].to]];//连边 56 } 57 for(int i=1;i<=num;++i) 58 if(sum[i]==0) que[++top]=i,ans[que[top]]+=w2[que[top]]; 59 for(int i=1;i<=top;++i) 60 { 61 for(int j=h[1][que[i]];j;j=e[1][j].nex) 62 { 63 sum[e[1][j].to]--; 64 if(!sum[e[1][j].to]) que[++top]=e[1][j].to; 65 } 66 }//拓扑排序 67 } 68 void solve()//裸DP 69 { 70 for(int i=1;i<=top;++i) 71 for(int j=h[1][que[i]];j;j=e[1][j].nex) 72 ans[e[1][j].to]=MAX(ans[e[1][j].to],w2[e[1][j].to]+ans[que[i]]); 73 int maxx=0; 74 for(int i=1;i<=num;++i) 75 maxx=max(maxx,ans[i]); 76 printf("%d",maxx); 77 } 78 void input() 79 { 80 r(n),r(m); 81 for(re int i=1;i<=n;++i) 82 r(w[i]); 83 for(re int i=1;i<=m;++i) 84 r(u),r(to),add(u,to,0); 85 } 86 int main() 87 { 88 input(); 89 for(re int i=1;i<=n;++i) 90 if(!id[i]) 91 tarjan(i); 92 build(); 93 solve(); 94 return 0; 95 }