#1185 : 连通性·三
描述
暑假到了!!小Hi和小Ho为了体验生活,来到了住在大草原的约翰家。今天一大早,约翰因为有事要出去,就拜托小Hi和小Ho忙帮放牧。
约翰家一共有N个草场,每个草场有容量为W[i]的牧草,N个草场之间有M条单向的路径。
小Hi和小Ho需要将牛羊群赶到草场上,当他们吃完一个草场牧草后,继续前往其他草场。当没有可以到达的草场或是能够到达的草场都已经被吃光了之后,小hi和小Ho就把牛羊群赶回家。
一开始小Hi和小Ho在1号草场,在回家之前,牛羊群最多能吃掉多少牧草?
举个例子:
图中每个点表示一个草场,上部分数字表示编号,下部分表示草场的牧草数量w。
在1吃完草之后,小Hi和小Ho可以选择把牛羊群赶到2或者3,假设小Hi和小Ho把牛羊群赶到2:
吃完草场2之后,只能到草场4,当4吃完后没有可以到达的草场,所以小Hi和小Ho就把牛羊群赶回家。
若选择从1到3,则可以到达5,6:
选择5的话,吃完之后只能直接回家。若选择6,还可以再通过6回到3,再到5。
所以该图可以选择的路线有3条:
1->2->4 total: 11 1->3->5 total: 9 1->3->6->3->5: total: 13
所以最多能够吃到的牧草数量为13。
本题改编自USACO月赛金组
输入
第1行:2个正整数,N,M。表示点的数量N,边的数量M。1≤N≤20,000, 1≤M≤100,000
第2行:N个正整数,第i个整数表示第i个牧场的草量w[i]。1≤w[i]≤100,000
第3..M+2行:2个正整数,u,v。表示存在一条从u到v的单向路径。1≤u,v≤N
输出
第1行:1个整数,最多能够吃到的牧草数量。
代码
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #include<stack> 6 #include<queue> 7 #include<algorithm> 8 using namespace std; 9 const int N=2e4+5; 10 11 vector<int>v[N]; 12 vector<int>gra[N]; 13 stack<int>sk; 14 int n,m,cnt,num,ans; 15 int val[N],low[N],dfn[N],fa[N],sum[N],indeg[N],dp[N]; 16 bool vis[N]; 17 18 //求出强连通分量 19 void tarjan(int u){ 20 low[u]=dfn[u]=++cnt; 21 sk.push(u); 22 for(int i=0;i<v[u].size();i++){ 23 int t=v[u][i]; 24 if(!dfn[t]){ 25 tarjan(t); 26 low[u]=min(low[u],low[t]); 27 } 28 else if(!fa[t]) low[u]=min(low[u],dfn[t]); 29 } 30 if(low[u]==dfn[u]){ 31 num++; 32 while(!sk.empty()){ 33 int t=sk.top(); 34 sk.pop(); 35 fa[t]=num; 36 sum[num]+=val[t]; 37 if(t==u) break; 38 } 39 } 40 } 41 42 //拓扑排序求最大价值 43 int topo(){ 44 queue<int>q; 45 for(int i=1;i<=num;i++){ 46 if(indeg[i]==0) 47 q.push(i); 48 } 49 int ans=0; 50 while(!q.empty()){ 51 int k=q.front(); 52 q.pop(); 53 dp[k]+=sum[k]; 54 ans=max(dp[k],ans); 55 for(int i=0;i<gra[k].size();i++){ 56 int t=gra[k][i]; 57 indeg[t]--; 58 dp[t]=max(dp[t],dp[k]); 59 if(indeg[t]==0) 60 q.push(t); 61 } 62 } 63 return ans; 64 } 65 66 int main(){ 67 scanf("%d%d",&n,&m); 68 for(int i=1;i<=n;i++) scanf("%d",&val[i]); 69 for(int i=1;i<=m;i++){ 70 int a,b; 71 scanf("%d%d",&a,&b); 72 v[a].push_back(b); 73 } 74 tarjan(1); //起点是1 75 //建新图 76 for(int i=1;i<=n;i++){ 77 if(fa[i]==0) continue; 78 for(int j=0;j<v[i].size();j++){ 79 int t=v[i][j]; 80 if(fa[i]!=fa[t]) 81 gra[fa[i]].push_back(fa[t]); 82 } 83 } 84 //计算入度 85 for(int i=1;i<=num;i++){ 86 for(int j=0;j<gra[i].size();j++){ 87 int t=gra[i][j]; 88 indeg[t]++; 89 } 90 } 91 //利用拓扑排序算出答案 92 printf("%d\n",topo()); 93 return 0; 94 }