题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1179
个人感觉此题比省选题简单多了,大概是POJ中档题的难度。。。
首先我们把这个有向图缩个点,缩点后的图是个DAG,新图中每个点的权值是对应强连通分量中的点的权值之和,新图中每个点对应的强连通分量中的点都是相互可达的,也就是说新图中的每个点,劫匪都能一次性抢完其中所有ATM机的钱。
然后对这个新图跑SPFA,SPFA本来是搞边权的,在这个题里,把SPFA稍微改改就能跑点权了,类似于NOIP的最优贸易那个题,很简单。
最后枚举所有终点,找出能够得到最多钱的那个终点就行了。
#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> #define MAXE 500050 #define MAXV 500050 using namespace std; struct edge { int u,v,next; }edges1[MAXE],edges2[MAXE]; //缩点前的图、缩点后的图 int nCount1=0,nCount2=0,head1[MAXV],head2[MAXV],money[MAXV]; //money[i]=原图中点i的钱数 int dfn[MAXV],low[MAXV],cnt=0,belong[MAXV],value[MAXV],tot=0; //value[i]=第i号联通块中的ATM机金额总数,tot=强连通分量总数 bool visit[MAXV]; int stack[MAXV],top=0; int n,m,S,P; void AddEdge1(int U,int V) { edges1[++nCount1].u=U; edges1[nCount1].v=V; edges1[nCount1].next=head1[U]; head1[U]=nCount1; } void AddEdge2(int U,int V) { edges2[++nCount2].u=U; edges2[nCount2].v=V; edges2[nCount2].next=head2[U]; head2[U]=nCount2; } void tarjan(int u) { low[u]=dfn[u]=++cnt; visit[u]=true; stack[++top]=u; for(int p=head1[u];p!=-1;p=edges1[p].next) { int v=edges1[p].v; if(!dfn[v]) { tarjan(v); low[u]=min(low[u],low[v]); } else if(visit[v]) low[u]=min(low[u],dfn[v]); } if(dfn[u]==low[u]) { tot++; int v=-1; while(u!=v) { v=stack[top--]; belong[v]=tot; value[tot]+=money[v]; visit[v]=false; } } } void rebuild() //缩点建立新图 { for(int u=1;u<=n;u++) for(int p=head1[u];p!=-1;p=edges1[p].next) { int v=edges1[p].v; if(belong[u]!=belong[v]) AddEdge2(belong[u],belong[v]); } } int f[MAXV],q[MAXV]; //f[i]=从起点到点i时抢到的最多钱数 bool inQueue[MAXV]; void SPFA() { int h=0,t=1; q[h]=belong[S]; inQueue[belong[S]]=true; f[belong[S]]=value[belong[S]]; while(h<t) { int u=q[h++]; inQueue[u]=false; if(h==MAXV-1) h=0; //循环利用队列,防止RE for(int p=head2[u];p!=-1;p=edges2[p].next) { int v=edges2[p].v; if(f[u]+value[v]>f[v]) { f[v]=f[u]+value[v]; if(!inQueue[v]) { inQueue[v]=true; q[t++]=v; if(t==MAXV-1) t=0; //循环利用队列,防止RE } } } } } int main() { int ans=0; memset(head1,-1,sizeof(head1)); memset(head2,-1,sizeof(head2)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); AddEdge1(u,v); } for(int i=1;i<=n;i++) scanf("%d",&money[i]); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); scanf("%d%d",&S,&P); rebuild(); //建立缩点后的新图 SPFA(); for(int i=1;i<=P;i++) { int x; scanf("%d",&x); if(f[belong[x]]>ans) ans=f[belong[x]]; } printf("%d\n",ans); return 0; }