可以多次走一个点或一条边,但只计算一次点权,意味着我们需要进行缩点
tarjan进行求强联通分量主要记录DFN[i],LOW[i],以及维护que记录走过的点
通过后向边和树枝边对LOW值的更新,当DFN[i]=LOW[i]时,就出现了强连通分量,可以进行缩点操作
在缩点后,重新建图,然后就可以通过dp(记忆化) 进行统计
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
const int maxm=1e5+5;
int n,m,tot,inin[maxn],dp[maxn],ans,color[maxn],va[maxn],LOW[maxn],times,top,cnt,que[maxn],DFN[maxn],head[maxn],val[maxn],ux[maxm],uy[maxm];
struct edge
{
int to,nxt;
}G[maxm<<1];
void add(int x, int y)
{
G[++cnt].to=y; G[cnt].nxt=head[x]; head[x]=cnt;
}
void tarjan(int x)
{
que[++top]=x;
inin[x]=1;
DFN[x]=LOW[x]=++times;
for(int i=head[x];i;i=G[i].nxt)
{
int to=G[i].to;
if(!DFN[to])
{
tarjan(to);
LOW[x]=min(LOW[x],LOW[to]);
}
else if(inin[to])
LOW[x]=min(LOW[x],DFN[to]);
}
if(DFN[x]==LOW[x])
{
tot++;
while(que[top+1]!=x)
{
color[que[top]]=tot;
va[tot]+=val[que[top]];
inin[que[top--]]=0;
}
}
}
void dpp(int x)
{
if(dp[x]) return ;
dp[x]=va[x];
int maxsum=0;
for(int i=head[x];i;i=G[i].nxt)
{
int to=G[i].to;
if(!dp[to]) dpp(to);
maxsum=max(maxsum,dp[to]);
}
dp[x]+=maxsum;
}
int main()
{
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&val[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ux[i],&uy[i]);
add(ux[i],uy[i]);
}
for(int i=1;i<=n;i++)
if(!DFN[i]) tarjan(i);
memset(head,0,sizeof(head));
cnt=0;
for(int i=1;i<=m;i++)
if(color[ux[i]]!=color[uy[i]])
add(color[ux[i]],color[uy[i]]);
for(int i=1;i<=tot;i++)
{
if(!dp[i])
{
dpp(i);
ans=max(ans,dp[i]);
}
}
printf("%d\n",ans);
return 0;
}

本文深入探讨了Tarjan算法在求解强连通分量中的应用,并结合缩点DP技术,详细讲解了如何在图论问题中高效地进行状态转移和点权计算,以实现最优解。
537

被折叠的 条评论
为什么被折叠?



