题目大意:一个有向图,每个顶点有一个权值(可正可负),现在从某个顶点出发,求所能获得的最大权值。当经过某个顶点时可以选择加上该顶点的权值或者不加,且一个顶点的权值只能够被加一次,顶点可重复访问。
建模:显然对于权为负的顶点是不加的,对于一个强连通分支,要使获得的权值最大,所有的正权都应当算上。于是可以把原图进行缩点,对于每一个强连通分量其权值为内部点的正权值之和。缩点后得到一个DAG图,用SPFA求最长路即可。这里有个技巧,就是加一个源点,另其权值为0,对于DAG图每个顶点连一条边。这样只需以该源点为起点,求一次SPFA就可以得到从各个顶点出发获得的最大值。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<stack> #include<queue> using namespace std; #define N 30005 struct Edge{ int to,next; }edge[150005]; int cnt,head[N],low[N],dfn[N],dfs_clock,scc_cnt,sccno[N],val[N],v[N]; vector<int>scc[N]; stack<int>S; void dfs(int u){ low[u]=dfn[u]=++dfs_clock; S.push(u); for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(!dfn[v]){ dfs(v); low[u]=min(low[v],low[u]); } else if(!sccno[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ val[++scc_cnt]=max(0,v[u]); for(;;){ int x=S.top();S.pop(); sccno[x]=scc_cnt; if(x==u) break; val[scc_cnt]+=max(0,v[x]); } } } void find_scc(int n){ memset(sccno,0,sizeof(sccno)); memset(dfn,0,sizeof(dfn)); dfs_clock=scc_cnt=0; for(int i=0;i<n;++i) if(!dfn[i]) dfs(i); } inline void add(int u,int v){ edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } vector<int>G[N]; bool inq[N]; int d[N]; void spfa(int s){ queue<int>q; memset(inq,0,sizeof(inq)); memset(d,0,sizeof(d)); inq[s]=1;q.push(s); while(!q.empty()){ int u=q.front();q.pop(); inq[u]=0; for(int i=0;i<G[u].size();++i){ int v=G[u][i]; if(d[u]+val[v]>d[v]){ d[v]=d[u]+val[v]; if(!inq[v]){ inq[v]=1; q.push(v); } } } } } int main() { int i,j,n,m,x,y; while(~scanf("%d%d",&n,&m)){ memset(head,-1,sizeof(head)); cnt=0; for(i=0;i<n;++i) scanf("%d",&v[i]); for(i=0;i<m;++i){ scanf("%d%d",&x,&y); add(x,y); } find_scc(n); for(i=1;i<=scc_cnt;++i) {G[i].clear();d[i]=-1;} for(i=0;i<n;++i) for(j=head[i];~j;j=edge[j].next){ int v=edge[j].to; if(sccno[i]!=sccno[v]) G[sccno[i]].push_back(sccno[v]); } for(i=1;i<=scc_cnt;++i) G[scc_cnt+1].push_back(i); int ans=-1; spfa(scc_cnt+1); for(i=1;i<=scc_cnt;++i) ans=max(ans,d[i]); printf("%d\n",ans); } return 0; }