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;
}

浙公网安备 33010602011771号