给定一个 nn 个点 mm 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。
允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
第一行两个正整数 n,mn,m
第二行 nn 个整数,依次代表点权
第三至 m+2m+2 行,每行两个整数 u,vu,v,表示一条 u\rightarrow vu→v 的有向边。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=10005; ll n,m,sum,tim,top,s; ll p[maxn],head[maxn],sd[maxn],dfn[maxn],low[maxn]; ll stac[maxn],vis[maxn]; ll h[maxn],in[maxn],dist[maxn]; struct st { int to; int next; int from; }edge[maxn*10],ed[maxn*10]; void add(int x,int y){ edge[++sum].next=head[x]; edge[sum].from=x; edge[sum].to=y; head[x]=sum; } void tarjan(int x){ low[x]=dfn[x]=++tim; stac[++top]=x; vis[x]=1; for(int i=head[x];i;i=edge[i].next){ int v=edge[i].to; if(!dfn[v]){ tarjan(v); low[x]=min(low[x],low[v]); } else if(vis[v]){ low[x]=min(low[x],low[v]); } } if(dfn[x]==low[x]){ int y; while(y=stac[top--]){ sd[y]=x; vis[y]=0; if(x==y){ break; } p[x]+=p[y]; } } } ll topo(){ queue<int >q; int tot=0; for(int i=1;i<=n;i++){ if(sd[i]==i&&!in[i]){ q.push(i); dist[i]=p[i]; } } while(!q.empty()){ int temp=q.front(); q.pop(); for(int i=h[temp];i;i=ed[i].next){ int v=ed[i].to; dist[v]=max(dist[v],dist[temp]+p[v]); in[v]--; if(in[v]==0)q.push(v); } } ll ans=0; for(int i=1;i<=n;i++){ ans=max(ans,dist[i]); } return ans; } void solve(){ cin>>n>>m; for(int i=1;i<=n;i++)cin>>p[i]; for(int i=1;i<=m;i++){ ll u,v; cin>>u>>v; add(u,v); } for(int i=1;i<=n;i++){ if(!dfn[i]){ tarjan(i); } } for(int i=1;i<=m;i++){ int x=sd[edge[i].from],y=sd[edge[i].to]; if(x!=y){ ed[++s].next=h[x]; ed[s].to=y; ed[s].from=x; h[x]=s; in[y]++; } } cout<<topo()<<endl; } int main(){ ios::sync_with_stdio(false);cin.tie(0); // freopen("1.in","r",stdin); // getlist(1e7); int t=1; //cin>>t; while(t--){ solve(); } }
原文:https://www.cnblogs.com/LH2000/p/14823879.html