Loading

P3387 【模板】缩点(Tarjan求强连通分量)

题目背景

缩点+DP

题目描述

给定一个 nnn 个点 mmm 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

输入格式

第一行两个正整数 n,mn,mn,m

第二行 nnn 个整数,依次代表点权

第三至 m+2m+2m+2 行,每行两个整数 u,vu,vu,v,表示一条 u→vu\rightarrow vuv 的有向边。

输出格式

共一行,最大的点权之和。

输入输出样例

输入 #1
2 2
1 1
1 2
2 1
输出 #1
2
板子题因为脑瘫错误调了半天...
注意到题干描述,很容易看出是要缩点,跑一遍Tarjan后我们就得到了一个DAG,DAG有点类似树的结构,只需要从每个入度为0的点(有点类似树根)跑dfs就行了,记得记忆化。
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
const int M = 100005;
int ver[M],Next[M],head[N],dfn[N]={0},low[N];
int vc[M],nc[M],hc[N],tc=0;
int sstack[N],ins[N],c[N];
int point[N],point_c[N]={0};
vector<int>scc[N];
int n,m,tot=0,num=0,top=0,cnt=0;
int ans[N]={0};
void add(int x, int y)
{
    ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}
void add_c(int x,int y)
{
    vc[++tc]=y,nc[tc]=hc[x],hc[x]=tc;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    sstack[++top]=x,ins[x]=1;
    int i;
    for(i=head[x];i;i=Next[i])
    {
        if(!dfn[ver[i]])
        {
            tarjan(ver[i]);
            low[x]=min(low[x],low[ver[i]]);
        }
        else if(ins[ver[i]]) low[x]=min(low[x],dfn[ver[i]]);
    }
    if(dfn[x]==low[x])
    {
        cnt++;
        int y;
        do
        {
            y=sstack[top--],ins[y]=0;
            c[y]=cnt,scc[cnt].push_back(y);
        }while(x!=y);
    }
}
void dfs(int x,int pre)//得用缩点后的新图 
{
    int i;
    ans[x]=max(ans[x],ans[pre]+point_c[x]);
    for(i=hc[x];i;i=nc[i])
    {
        if(vc[i]!=pre)dfs(vc[i],x);
    }
}
int main()
{
    cin>>n>>m;
    int i,j,x;
    for(i=1;i<=n;i++)scanf("%d",&point[i]);
    for(i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
    }
    for(i=1;i<=n;i++)
    {
        if(!dfn[i])tarjan(i);
    } 
    int deg[N]={0}; 
    for(x=1;x<=n;x++)
    {
        for(i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if(c[x]==c[y])continue;
            add_c(c[x],c[y]);
            deg[c[y]]++;//deg表示入度 别写成deg[y]++ 
        }
    }
    for(i=1;i<=cnt;i++)
    {
        for(j=0;j<scc[i].size();j++)
        {
            point_c[i]+=point[scc[i][j]];
        }
    }
    //新图是一个有向无环图 找入度为0的点
    ans[0]=0;
    for(i=1;i<=cnt;i++)
    {
        
        if(deg[i]==0)//不能只从入度为0的点开始遍历 
        {
            dfs(i,0);
        }
    }
    int mmax=0;
    for(i=1;i<=cnt;i++)
    {
        mmax=max(mmax,ans[i]);
    }
    cout<<mmax<<endl;
    return 0;
}

 

posted @ 2020-05-03 11:00  脂环  阅读(251)  评论(0编辑  收藏  举报