Loading

P3387 【模板】缩点 题解 (Tarjan)

题目链接

P3387 【模板】缩点

解题思路

这几天搞图论,好有趣hhh,多写几篇博客。

上次学\(Tarjan\)求割点,这次缩点。

思路大概是多一个栈和染色的步骤,每次\(Tarjan\)的时候把点入栈,如果某个点(比较像割点但不完全是)的\(DFS\)子树都搜不到它祖宗,那么接下来进行的遍历操作必然与该点不能形成强连通分量,所以可以遇到\(low[p]==dfn[p]\)的点就把栈里面的东西全弹出来,染色表示这是一个强连通分量。

对于这个题,强连通分量之间再进行连边,记忆化搜索(类似树形DP)即可。

AC代码

#include<stdio.h>
#include<string.h>
#define N 100010
#define M 10010
#define min(a,b) (a>b?b:a)
#define max(a,b) (a<b?b:a)
int n,m;
int v[M],x[N],y[N];

struct Edge{
	int end,near;
}e[N];
int head[M],cnt;
void add(int a,int b){
	e[++cnt].end=b;
	e[cnt].near=head[a];
	head[a]=cnt;
}

int dfn[M],tot,sta[M],top,vis[M],low[M];
int color[M],num,val[M];
void tarjan(int p){
	dfn[p]=low[p]=++tot;
	sta[++top]=p;vis[p]=1;
	int i;
	for(i=head[p];i;i=e[i].near){
		int q=e[i].end;
		if(!dfn[q])tarjan(q),low[p]=min(low[p],low[q]);
		else if(vis[q])low[p]=min(low[p],low[q]);
	}
	if(dfn[p]==low[p]){
		num++;
		while(sta[top+1]!=p){
			color[sta[top]]=num;
			vis[sta[top]]=0;
			val[num]+=v[sta[top]];
			top--;
		}
	}
}

int f[M];
void dp(int p){
	int i;
	for(i=head[p];i;i=e[i].near){
		int q=e[i].end;
		if(!f[q])dp(q);
		f[p]=max(f[p],f[q]);
	}
	f[p]+=val[p];
}

int main(){
	int i,ans=0;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++)scanf("%d",&v[i]);
	for(i=0;i<m;i++)scanf("%d%d",&x[i],&y[i]),add(x[i],y[i]);
	for(i=1;i<=n;i++)
		if(!dfn[i])tarjan(i);//tarjan缩点 
	memset(head,0,sizeof(head));cnt=0;//清空边 
	for(i=0;i<m;i++)//强连通分量之间连边 
		if(color[x[i]]^color[y[i]])add(color[x[i]],color[y[i]]);
	for(i=1;i<=num;i++)if(!f[i])dp(i),ans=max(ans,f[i]);//DP
	printf("%d",ans);
	return 0;
}
posted @ 2019-02-27 15:34  Potassium  阅读(289)  评论(0编辑  收藏  举报