题目描述
某人准备从1号节点出发开始抢劫,并在一个有酒吧的节点停止抢劫,已知每个节点ATM机拥有的钱数,求最多抢劫的钱数(可重复经过道路,抢完后ATM机没钱)。
思路
我们考虑对于一个强连通分量,他必定可以抢完这个强连通分量中所有的钱并到达任意节点,因此我们可以缩点。缩点之后每个点的点权即为这个强连通分量的总金额。我们对于缩点后的DAG,显然可以将点权转化为边权,在这张图上找最长路。这里最长路我们可以用spfa实现,这里我们避免用拓扑,因为用拓扑排序时我们只能先加入起点,而不能加入其它度为0的点,否则会到达无法到达的点,在实现上比较复杂,而直接最短路实际并不比拓扑慢多少。
代码
#include <bits/stdc++.h> using namespace std; const int N=5e5+10,M=5e5+10; struct Edge { int x,y; }e[M]; int v[N]; int nxt[M],to[M],head[N],w[N],tot; void add_edge(int x,int y,int v) { nxt[++tot]=head[x]; head[x]=tot; to[tot]=y; w[tot]=v; } int read() { int res=0,w=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){res=(res<<3)+(res<<1)+ch-‘0‘;ch=getchar();} return res*w; } int dfn[N],low[N],st[N],co[N],sum[N],top,col,idx; void tarjan(int u) { dfn[u]=low[u]=++idx; 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],dfn[v]); } if(dfn[u]==low[u]) { co[u]=++col; sum[col]=v[u]; while(st[top]!=u) { co[st[top]]=col; sum[col]+=v[st[top]]; --top; } --top; } } int m; void rebuild() { tot=0; memset(head,0,sizeof(head)); for(int i=1;i<=m;i++) { int u=co[e[i].x],v=co[e[i].y]; if(u!=v) add_edge(u,v,sum[v]); } } int dis[N]; bool exist[N]; void spfa(int s) { queue<int>q; dis[s]=sum[s];exist[s]=1; q.push(s); while(!q.empty()) { int u=q.front();q.pop(); exist[u]=0; for(int i=head[u];i;i=nxt[i]) { int v=to[i]; if(dis[v]<dis[u]+w[i]) { dis[v]=dis[u]+w[i]; if(!exist[v]) { exist[v]=1; q.push(v); } } } } } int ed[N]; int main() { int n,s,p; n=read();m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); add_edge(x,y,0); e[i].x=x;e[i].y=y; } for(int i=1;i<=n;i++) v[i]=read(); s=read();p=read(); for(int i=1;i<=p;i++) ed[i]=read(); for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); rebuild(); spfa(co[s]); int ans=0; for(int i=1;i<=p;i++) ans=max(ans,dis[co[ed[i]]]); printf("%d",ans); }
原文地址:https://www.cnblogs.com/fangbozhen/p/11728898.html
时间: 2024-10-11 12:14:27