题目链接
解题思路
这几天搞图论,好有趣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;
}
原文地址:https://www.cnblogs.com/Potassium/p/10444067.html
时间: 2024-11-10 13:00:40