1. 程式人生 > >CDOJ 1962 天才錢vs學霸周2【最大流】

CDOJ 1962 天才錢vs學霸周2【最大流】

IT min AD true class tin edm names nds

以s=0,t=n+m+1分別為超級源點和超級匯點。網絡流中的流量以0為開始,題目要求從1到20,我們先把每個點都減去1,即ai - m,bi - n。然後源點s與n個頂點連容量為ai的路,匯點t與m個頂點連容量為bi的路,n個頂點再與m個頂點連接19的容量。最後再跑下Dinic,如果最後匯聚到t的流量和總值相同,輸出“Yes”,否則輸出“No”。最後輸出對應邊的答案,可以用殘余網絡來計算,用19-殘余的容量即該條邊的流量。

  1 #include <queue>
  2 #include <cstdio>
  3 #include <cstring>
  4
#include <iostream> 5 #include <algorithm> 6 using namespace std; 7 8 const int N=100; 9 const int M=N*N; 10 const int INF = 0x3f3f3f3f; 11 int s,t,n,m,cnt; 12 int a[N],b[N]; 13 int Head[N],Depth[N],Next[M],V[M],W[M]; 14 15 void init() 16 { 17 cnt=-1; 18
memset(Head,-1,sizeof(Head)); 19 memset(Next,-1, sizeof(Next)); 20 } 21 22 void add_edge(int u,int v,int w) 23 { 24 cnt++;Next[cnt]=Head[u];V[cnt]=v;W[cnt]=w;Head[u]=cnt; 25 cnt++;Next[cnt]=Head[v];V[cnt]=u;W[cnt]=0;Head[v]=cnt; // 反向邊 26 } 27 28 bool bfs() 29 {
30 queue<int> q; 31 while(!q.empty())q.pop(); 32 memset(Depth,0, sizeof(Depth)); 33 q.push(s); 34 Depth[s]=1; 35 while(!q.empty()) 36 { 37 int u=q.front();q.pop(); 38 for(int i=Head[u];i!=-1;i=Next[i]) 39 { 40 if(W[i]>0&&Depth[V[i]]==0) 41 { 42 Depth[V[i]]=Depth[u]+1; 43 q.push(V[i]); 44 } 45 } 46 } 47 if(Depth[t]==0)return false; 48 else return true; 49 } 50 51 int dfs(int u,int flow) 52 { 53 if(u==t)return flow; 54 int rest = flow; 55 for(int i=Head[u];i!=-1;i=Next[i]) 56 { 57 if(Depth[V[i]]==Depth[u]+1&&W[i]>0) 58 { 59 int k=dfs(V[i],min(W[i],rest)); 60 if(!k)Depth[V[i]]=0; // 剪枝,去掉增廣完畢的點 61 W[i]-=k; 62 W[i^1]+=k; 63 rest-=k; 64 } 65 } 66 return flow-rest; 67 } 68 69 int Dinic() 70 { 71 int ans=0; 72 while(bfs()) // 在殘量網絡上構造分層圖 73 { 74 while(int flow = dfs(s,INF)) // 在當前分層圖上增廣 75 ans+=flow; 76 } 77 return ans; 78 } 79 80 void print() 81 { 82 int res[N][N]; 83 memset(res,0,sizeof(res)); 84 for(int i=1;i<=n;i++) 85 for(int j=Head[i];j!=-1;j=Next[j]) 86 { 87 int v=V[j]; 88 if(v>n&&v<=n+m)res[i][v-n]=19-W[j]+1; 89 } 90 for(int i=1;i<=n;i++){ 91 for(int j=1;j<=m;j++){ 92 printf("%d",res[i][j]); 93 if(j==m) printf("\n"); 94 else printf(" "); 95 } 96 } 97 } 98 99 int main(){ 100 init(); 101 scanf("%d%d",&n,&m); 102 s=0;t=n+m+1; 103 104 for(int i=1;i<=n;i++){ 105 scanf("%d",&a[i]);a[i]-=m; 106 add_edge(s,i,a[i]); 107 } 108 for(int i=1;i<=m;i++){ 109 scanf("%d",&b[i]);b[i]-=n; 110 add_edge(n+i,t,b[i]); 111 } 112 113 for(int i=1;i<=n;i++) 114 for(int j=1;j<=m;j++) 115 add_edge(i,n+j,19); 116 117 int c1=0,c2=0,c3=0; 118 for(int i=1;i<=n;i++) c1+=a[i]; 119 for(int i=1;i<=m;i++) c2+=b[i]; 120 if(c1!=c2) printf("No\n"); 121 else{ 122 c3=Dinic(); 123 if(c3!=c2) printf("No\n"); 124 else{ 125 printf("Yes\n"); 126 print(); 127 } 128 } 129 return 0; 130 }

附:Edmonds-Karp增廣路算法模板: 不斷用BFS尋找增廣路,直至網絡上不存在增廣路為止

 1 const int inf = 1<<29,N=2010,M=20010;
 2 int head[N],ver[M],edge[M],Next[M],v[N],incf[N],pre[N];
 3 int n,m,s,t,tot,maxflow;
 4 
 5 void add(int x,int y,int z)
 6 {
 7     tot++,ver[tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot; // 鄰接表的數組寫法
 8     tot++,ver[tot]=x,edge[tot]=0,Next[tot]=head[y],head[y]=tot; //反向邊,edge剩余容量
 9 }
10 
11 bool bfs()
12 {
13     memset(v,0, sizeof(v));
14     queue<int> q;
15     q.push(s);v[s]=1;
16     incf[s]=inf; // 增廣路上個邊的最小剩余容量
17     while(q.size())
18     {
19         int x = q.front();
20         q.pop();
21         for(int i=head[x];i;i=Next[i])
22         {
23             if(edge[i])
24             {
25                 int y=ver[i];
26                 if(v[y])continue;
27                 incf[y]=min(incf[x],edge[i]);
28                 pre[y]=i; // 記錄前驅(當前的tot序號,偶奇成對存儲,抑或操作可導出前驅)
29                 q.push(y);
30                 v[y]=1;
31                 if(y==t)return 1; // 可達t點
32             }
33         }
34     }
35     return 0;
36 }
37 
38 void update()// 更新一條增廣路及其反向邊的剩余容量
39 {
40     int x = t;
41     while(x!=s)
42     {
43         int i=pre[x];
44         edge[i]-=incf[t];
45         edge[i^1]+=incf[t];
46         x = ver[i^1]; // 前驅節點
47     }
48     maxflow+=incf[t];
49 }
50 
51 int main()
52 {
53     while(cin>>m>>n)
54     {
55         memset(head,0, sizeof(head));
56         s=1,t=n;tot=1;maxflow=0;
57         for(int i=1;i<=m;i++)
58         {
59             int x,y,c;
60             scanf("%d%d%d",&x,&y,&c);
61             add(x,y,c);
62         }
63         while(bfs())update();
64         cout<<maxflow<<endl;
65     }
66 }

CDOJ 1962 天才錢vs學霸周2【最大流】