CDOJ 1962 天才钱vs学霸周2【最大流】
以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 }