在我们求最大路径的时候,我们应该不希望有环出现,否则的话我们可能走着走着就走回自己了么让我们需要用到我走到的点的值去更新之前的点的值,那么有环的情况下,我就要用自己的值来更新自己(其实是可以的,默认是0嘛)。但是嘛,这道题让我们可以重复经过一个点或者一条边,那么我们就什么也不用考虑了,在一个强连通分量中的点一定能互相到达,结果一定更优。(感觉自己是昧着良心说的,因为这道题不够严谨的是没强调点权是非负数,若存在负数的话,每一次都缩点不一定会有最优解)
既然这样,我们就姑且不在意这件事,毕竟我们现在学的是方法,暂且不计较。
那么,所有的强连通分量里面的点都可以互相到达,那么我们可以理解为,只要到了其中一个,就相当于可以到任何的其他一个,那为什么要每一次都重新跑一遍呢?这些点既然都可以互相到达,为什么不揉成一个点呢?
所以我们只要在求出所有的强连通分量之后,按照不同的颜色,重新建图即可,最后再跑一遍记忆化就可以啦~
代码如下:
#include<cstdio> #include<iostream> #include<cstring> using namespace std; #define maxn 2*100005 int head[maxn],w[maxn],st[maxn],co[maxn],dfn[maxn],low[maxn]; int am[maxn],an[maxn],sum[maxn],f[maxn],to[maxn],nxt[maxn]; int n,m,num,top,col,ans,cnt; void add(int a,int b) { to[++cnt]=b; nxt[cnt]=head[a]; head[a]=cnt; } void Tarjan(int u) { dfn[u]=low[u]=++num; st[++top]=u; for(int i=head[u]; i; i=nxt[i]) { int v=to[i]; if(!dfn[v]) { Tarjan(v); low[u]=min(low[u],low[v]); } else if(!co[v]) low[u]=min(low[u],low[v]); } if(low[u]==dfn[u]) { co[u]=++col; sum[col]+=w[u]; while(st[top]!=u) { sum[col]+=w[st[top]]; co[st[top--]]=col; } top--; } } void dfs(int u) { if(f[u]) return; f[u]=sum[u]; int now_max=0; for(int i=head[u]; i; i=nxt[i]) { int v=to[i]; dfs(v); now_max=max(now_max,f[v]); } f[u]+=now_max; } int main() { scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) scanf("%d",&w[i]); for(int i=1; i<=m; i++) { scanf("%d%d",&am[i],&an[i]); add(am[i],an[i]); } for(int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i); memset(head,0,sizeof(head)); memset(nxt,0,sizeof(nxt)); memset(to,0,sizeof(to)); for(int i=1; i<=m; i++) if(co[am[i]]!=co[an[i]]) add(co[am[i]],co[an[i]]); for(int i=1; i<=col; i++) { if(!f[i]) dfs(i); ans=max(ans,f[i]); } printf("%d",ans); return 0; }