tarjan 学习笔记

tarjan学习笔记

算法思想

通过一次dfs,找到所有的强连通分量。

分析与定义

dfn序:一个点被搜索到的顺序;

low:一个点通过最多一条非树边能到达的dfn最小的点;

栈:u的祖先和与u的祖先在同一强连通分量中的点。

我们把在dfs中找到的边分为三种:横叉边,返祖边和前向边。

对于前向边,我们把它加入搜索树,再以它为根节点进行搜索,记录并更新它的dfn值和low值,同时用它更新父节点的low值。

对于返祖边,说明找到了一个环,可以用它的值来更新父节点的low值,以此合并一条链。

对于横叉边,如果它在栈中,说明它一定在起点祖先的强连通图中,可以将其视作一条返祖边(因为它可以和起点的祖先强连通,而通过起点—终点和终点—祖先这两条路径可以使起点回到祖先;这里的low值的意义被淡化,只是作为辅助合并的标记)。

代码实现

#include<bits/stdc++.h>
using namespace std;
int head[10050],nxt[100050],to[100050],cnt;
int dfn[10050],low[10050],st[10050],top,tot;
int idx;
int a[10050],dp[10050];
int ans;
int bel[10050];
bool ins[10050];
void add(int u,int v){
	nxt[++cnt]=head[u],to[head[u]=cnt]=v;
}
void dfs(int u){
	low[u]=dfn[u]=++tot;
	ins[st[++top]=u]=1;
	for(int i=head[u];i;i=nxt[i]){
		
		int v=to[i];
		 if(!dfn[v]){
			dfs(v);
			low[u]=min(low[v],low[u]);
		}
		else if(ins[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u]){
		int v;++idx;
		
		int sum=0;
		do{
			ins[v=st[top--]]=0;
			bel[v]=idx;
			sum+=a[v];
			for(int i=head[v];i;i=nxt[i])
			{	
				dp[idx]=max(dp[idx],dp[bel[to[i]]]);
			}
		}while(v!=u);
		dp[idx]+=sum;
		ans=max(ans,dp[idx]);
	}
	
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);
	}
	
	for(int i=1;i<=n;i++){
		if(!dfn[i])dfs(i);
	}
	cout<<ans<<endl;
}
posted @ 2022-09-13 18:55  狐适之  阅读(55)  评论(1)    收藏  举报