学习笔记#缩点#tarjan

引入

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

思考

显然,如果选取了一个点,那么和这个点在一个强连通分量里的点都会被选取,因为点权都是正的,那么我们就找出所有的强连通分量,把一个强连通分量缩成一个点,再跑记搜就可以了

tarjan算法

之前在解决割点问题时已经介绍过这种算法了,没错,这种算法也可以求缩点,之前说过,核心是维护low和dfn数组,这里我们也是一样
显然,如果low[i]=dfn[i]那么i点一定与它的某一个儿子在同一强连通分量里,我们可以用一个栈来维护
对于单个点,本身就是一个强连通分量
找完强连通分量之后,我们重新建图,所以要把原来边的信息储存下来,如果2个点不在同一强连通分量里,就建一条边,之后就是记忆化搜索

ac代码

#include<bits/stdc++.h>
#define v a[i].to
using namespace std;
int n,m,x[100010],y[100010],head[10010],cnt,low[10010],dfn[10010],bl[10010],scc,sz[10010],f[10010],ans,sum[10010],val[10010];
stack<int>s;
struct edge{int to,next;}a[200010];
void add(int x,int y){a[++cnt]=(edge){y,head[x]},head[x]=cnt;}
void dfs(int u)
{
    low[u]=dfn[u]=++cnt,s.push(u);
    for(int i=head[u];i!=-1;i=a[i].next)
    {
        if(!low[v])dfs(v),low[u]=min(low[u],low[v]);
        else if(!bl[v])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        scc++;
        while(1)
        {
            sz[scc]++;
            int k=s.top();
            s.pop(),bl[k]=scc,sum[scc]+=val[k];
            if(k==u)break;
        }
    }
}
void search(int u)
{
    if(f[u])return;
    f[u]=sum[u];
    int maxx=0;
    for(int i=head[u];i!=-1;i=a[i].next)
    {
        if(!f[a[i].to])search(a[i].to);
        maxx=max(maxx,f[a[i].to]);
    }
    f[u]+=maxx;
}
int main()
{
    memset(head,-1,sizeof(head)),scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&val[i]);
    cnt=0;for(int i=1;i<=m;i++)scanf("%d%d",&x[i],&y[i]),add(x[i],y[i]);
    cnt=0;for(int i=1;i<=n;i++)if(!dfn[i])dfs(i);
    memset(head,-1,sizeof(head)),cnt=0;
    for(int i=1;i<=m;i++)if(bl[x[i]]!=bl[y[i]])add(bl[x[i]],bl[y[i]]);
    for(int i=1;i<=scc;i++){if(!f[i])search(i);ans=max(ans,f[i]);}
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值