题目背景
缩点+DP
题目描述
给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入输出格式
输入格式:第一行,n,m
第二行,n个整数,依次代表点权
第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式:共一行,最大的点权之和。
输入输出样例
说明
n<=10^4,m<=10^5,点权<=1000
算法:Tarjan缩点+DAGdp
好久没打过都忘了。。。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define M 100010 5 using namespace std; 6 int n,m,num,top,ans,cnt,tot; 7 int head[M],dfn[M],low[M],st[M]; 8 int f[M],sum[M],co[M],val[M],X[M],Y[M]; 9 bool vis[M]; 10 struct point{ 11 int to,next; 12 }e[M<<1]; 13 void add(int from,int to) 14 { 15 e[++num].next=head[from]; 16 e[num].to=to; 17 head[from]=num; 18 } 19 void tarjan(int x) 20 { 21 dfn[x]=low[x]=++cnt; 22 st[++top]=x; 23 vis[x]=true; 24 for(int i=head[x];i;i=e[i].next) 25 { 26 int to=e[i].to; 27 if(!dfn[to]) 28 { 29 tarjan(to); 30 low[x]=min(low[x],low[to]); 31 } 32 else if(vis[to]) low[x]=min(low[x],dfn[to]); 33 } 34 if(dfn[x]==low[x]) 35 { 36 ++tot; 37 vis[x]=false; 38 while(st[top+1]!=x) 39 { 40 co[st[top]]=tot; 41 vis[st[top]]=false; 42 sum[tot]+=val[st[top]]; 43 top--; 44 } 45 } 46 } 47 void dfs(int x) 48 { 49 f[x]=sum[x]; 50 int maxn=0; 51 for(int i=head[x];i;i=e[i].next) 52 { 53 int to=e[i].to; 54 if(!f[to]) dfs(to); 55 maxn=max(maxn,f[to]); 56 } 57 f[x]+=maxn; 58 } 59 int main() 60 { 61 scanf("%d%d",&n,&m); 62 for(int i=1;i<=n;i++) scanf("%d",&val[i]); 63 for(int i=1;i<=m;i++) 64 { 65 int x,y; scanf("%d%d",&x,&y); 66 X[i]=x; Y[i]=y; 67 add(x,y); 68 } 69 for(int i=1;i<=n;i++) 70 if(!dfn[i]) 71 tarjan(i); 72 num=0; 73 memset(e,0,sizeof(e)); 74 memset(head,0,sizeof(head)); 75 for(int i=1;i<=m;i++) 76 if(co[X[i]]!=co[Y[i]]) 77 add(co[X[i]],co[Y[i]]); 78 for(int i=1;i<=tot;i++) 79 if(!f[i]) 80 { 81 dfs(i); 82 ans=max(ans,f[i]); 83 } 84 printf("%d",ans); 85 return 0; 86 }