缩点
题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:
第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:
共一行,最大的点权之和。
输入输出样例
说明
n<=10^4,m<=10^5,点权<=1000
算法:Tarjan缩点+DAGdp
缩点板子题。
因为一个强连通分量上的点的可以相互到达,所以到达分量上任何一点就可以到达这个分量上的所有点,因此这个强连通分量就可以看做是一个整体。
又因为环一定是一个连通分量,所以缩点后图中将不存在环,则图会成为一个DAG,就可以dp做了。
#include<bits/stdc++.h> #define N 100500 using namespace std; vector<int>edges[N]; int dfn[N]={0},vis[N]={0},low[N],color[N],now_color=1,now_clock=1; int Stack[N],top=0; void dfs(int x) { vis[x]=1; dfn[x]=low[x]=now_clock++; Stack[++top]=x; int Size=edges[x].size(); for(int i=0;i<Size;i++) { int v=edges[x][i]; if(dfn[v]&&vis[v])low[x]=min(low[x],dfn[v]); else if(!dfn[v]) { dfs(v); low[x]=min(low[x],low[v]); } } if(dfn[x]==low[x]) { while(Stack[top]!=x) { color[Stack[top]]=now_color; vis[Stack[top]]=0; top--; } color[Stack[top]]=now_color; vis[Stack[top]]=0; top--; now_color++; } } vector<int>newedges[N]; int newa[N]={0}; int ans[N]={0}; int dfs2(int x) { if(ans[x])return ans[x]; int sum=0; int Size=newedges[x].size(); for(int i=0;i<Size;i++) sum=max(sum,dfs2(newedges[x][i])); return ans[x]=sum+newa[x]; } int main() { int n,m; int a[N]; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=m;i++) { int u,v; scanf("%d %d",&u,&v); edges[u].push_back(v); } for(int i=1;i<=n;i++)if(!dfn[i])dfs(i); for(int i=1;i<=n;i++) { int Size=edges[i].size(); for(int j=0;j<Size;j++) { int u=i,v=edges[i][j]; if(color[u]!=color[v])newedges[color[u]].push_back(color[v]); } newa[color[i]]+=a[i]; } int MAX=0; for(int i=1;i<now_color;i++) { if(!ans[i])dfs2(i); MAX=max(MAX,ans[i]); } printf("%d\n",MAX); return 0; }
路漫漫其修远兮,吾将上下而求索