poj3436+最大流
Description
As you know, all the computers used for ACM contests must be identical, so the participants compete on equal terms. That is why all these computers are historically produced at the same factory.
Every ACM computer consists of P parts. When all these parts are present, the computer is ready and can be shipped to one of the numerous ACM contests.
Computer manufacturing is fully automated by using N various machines. Each machine removes some parts from a half-finished computer and adds some new parts (removing of parts is sometimes necessary as the parts cannot be added to a computer in arbitrary order). Each machine is described by its performance (measured in computers per hour), input and output specification.
Input specification describes which parts must be present in a half-finished computer for the machine to be able to operate on it. The specification is a set of P numbers 0, 1 or 2 (one number for each part), where 0 means that corresponding part must not be present, 1 — the part is required, 2 — presence of the part doesn't matter.
Output specification describes the result of the operation, and is a set of P numbers 0 or 1, where 0 means that the part is absent, 1 — the part is present.
The machines are connected by very fast production lines so that delivery time is negligibly small compared to production time.
After many years of operation the overall performance of the ACM Computer Factory became insufficient for satisfying the growing contest needs. That is why ACM directorate decided to upgrade the factory.
As different machines were installed in different time periods, they were often not optimally connected to the existing factory machines. It was noted that the easiest way to upgrade the factory is to rearrange production lines. ACM directorate decided to entrust you with solving this problem.
Input
Input file contains integers PN, then N descriptions of the machines. The description of ith machine is represented as by 2 P + 1 integers QiSi,1Si,2...Si,PDi,1Di,2...Di,P, where Qi specifies performance, Si,j — input specification for part j, Di,k — output specification for part k.
Constraints
1 ≤ P ≤ 10, 1 ≤ N ≤ 50, 1 ≤ Qi ≤ 10000
Output
Output the maximum possible overall performance, then M — number of connections that must be made, then M descriptions of the connections. Each connection between machines A and B must be described by three positive numbers ABW, where W is the number of computers delivered from A to B per hour.
If several solutions exist, output any of them.
Sample input 1
3 4
15 0 0 0 0 1 0
10 0 0 0 0 1 1
30 0 1 2 1 1 1
3 0 2 1 1 1 1
Sample input 2
3 5
5 0 0 0 0 1 0
100 0 1 0 1 0 1
3 0 1 0 1 1 0
1 1 0 1 1 1 0
300 1 1 2 1 1 1
Sample input 3
2 2
100 0 0 1 0
200 0 1 1 1
Sample output 1
25 2
1 3 15
2 3 10
Sample output 2
4 5
1 3 3
3 5 3
1 2 1
2 4 1
4 5 1
Sample output 3
0 0
题意:有n台生产电脑的机器,每台电脑由m个部件组成,现告诉我们每台机器生产需要的部件(0代表不需要,1代表需要,2代表可要可不要)和每台机器能产出的部件(0代表不能产出,1代表能产出),当然如果产出的部件都为1,即能产出整台电脑。
求单位时间内的最大产量和有协作关系的机器的对数,并输出这些机器的编号及他们协作产出电脑的数量。
此题是一个最大流问题,关键在于如何构图。构图前先贴一下最大流的两个算法。
1.E-K算法
求最大流的过程,就是不断找到一条源到汇的路径,然后构建残余网络,再在残余网络上寻找新的路径,使总流量增加,然后形成新的残余网络,再寻找新路径…..直到某个残余网络上找不到从源到汇的路径为止,最大流就算出来了。每次寻找新流量并构造新残余网络的过程,就叫做寻找流量的“增广路径”,也叫“增广”。我们用bfs来寻找增广路,算法复杂度O(n*m*m),n为点数,m为边数。
1 int maxflow()//函数每次返回一个增广路上的最小容量,不断累加这个容量,直到返回值为0即可得到最大流 2 { 3 int i; 4 bool judge=false; 5 while(!q.empty()) q.pop();//q为普通的队列 6 memset(vis,0,sizeof(vis)); 7 memset(pre,0,sizeof(pre));//记录前驱 8 q.push(1);//源点入队 9 vis[1]=1; 10 pre[1]=0; 11 while(!q.empty()) 12 { 13 int z=q.front(); 14 q.pop(); 15 for(i=1;i<=n;i++) 16 { 17 if(g[z][i]>0&&!vis[i]) 18 { 19 pre[i]=z; 20 vis[i]=1; 21 if(i==n)//n为汇点,找到了1条源到汇的路径 22 { 23 judge=true; 24 break; 25 } 26 else q.push(i); 27 } 28 } 29 } 30 if(!judge) return 0;//找不到则返回0 31 int ret=mmax; 32 int v; 33 v=n; 34 while(pre[v])//找出路径上的最小容量 35 { 36 if(g[pre[v]][v]<ret) 37 ret=g[pre[v]][v]; 38 v=pre[v]; 39 } 40 v=n; 41 while(pre[v])//更新路径上的容量,原来的容量要减去最小容量,并增加1条同最小容量的反向边 42 { 43 g[pre[v]][v]-=ret; 44 g[v][pre[v]]+=ret; 45 v=pre[v]; 46 } 47 return ret; 48 }
2.Dinic算法
E-K算法的提高余地:需要多次从s到t调用BFS,可以设法减少调用次数。我们可以在1次增广过程中寻找多条增广路。
实现:bfs分层+dfs
我们用栈来实现dfs
利用 BFS对残余网络分层,分完层后,从源点开始,用DFS从前一层向后一层反复寻找增广路(即要求DFS的每一步都必须要走到下一层的节点)。
因此,前面在分层时,只要进行到汇点的层次数被算出即可停止,因为按照该DFS的规则,和汇点同层或更下一层的节点,是不可能走到汇点的。
DFS过程中,要是碰到了汇点,则说明找到了一条增广路径。此时要增加总流量的值,消减路径上各边的容量,并添加反向边,即所谓的进行增广。
DFS找到一条增广路径后,并不立即结束,而是回溯后继续DFS寻找下一个增广路径。
回溯到的节点u满足以下条件:
1) DFS搜索树的树边(u,v)上的容量已经变成0。即刚刚找到的增广路径上所增加的流量,等于(u,v)本次增广前的容量。(DFS的过程中,是从u走到更下层的v的)
2)u是满足条件 1)的最上层的节点
如果回溯到源点而且无法继续往下走了,DFS结束。
因此,一次DFS过程中,可以找到多条增广路径。
DFS结束后,对残余网络再次进行分层,然后再进行DFS
当残余网络的分层操作无法算出汇点的层次(即BFS到达不了汇点)时,算法结束,最大流求出。
一般用栈实现DFS,这样就能从栈中提取出增广路径。
算法复杂度O(n*n*m) ,n为点数,m为边数。
如果要求最大流中每条边的流量,将原图备份,原图上的边的容量减去做完最大流的残余网络上的边的剩余容量,就是边的流量。
1 bool countlayer()//分层函数 2 { 3 int i; 4 memset(layer,-1,sizeof(layer)); 5 deque<int> q;//双端队列,此时表示的是队列 6 layer[1]=0; 7 q.push_back(1); 8 while(!q.empty()) 9 { 10 int z=q.front(); 11 q.pop_front(); 12 for(i=1;i<=n;i++) 13 { 14 if(g[z][i]>0&&layer[i]==-1) 15 { 16 layer[i]=layer[z]+1; 17 if(i==n) return true;//汇点层数能求出,返回真值 18 else q.push_back(i); 19 } 20 } 21 } 22 return false; 23 } 24 int maxflow() 25 { 26 deque<int> q;//双端队列,此时表示的是栈 27 int i,ans=0;//ans记录最大流 28 while(countlayer())//只要能分层 29 { 30 q.push_back(1); 31 memset(vis,0,sizeof(vis)); 32 vis[1]=1; 33 while(!q.empty()) 34 { 35 int z=q.back(); 36 if(z==n)//栈顶元素为汇点 37 { 38 int ret=mmax;//记录增广路上的最小流量边 39 int retu;//最小流量边的起点 40 for(i=1;i<q.size();i++) 41 { 42 if(g[q[i-1]][q[i]]>0&&g[q[i-1]][q[i]]<ret) 43 { 44 ret=g[q[i-1]][q[i]]; 45 retu=q[i-1]; 46 } 47 } 48 ans+=ret;//加入最大流 49 for(i=1;i<q.size();i++)//更新流量 50 { 51 g[q[i-1]][q[i]]-=ret; 52 g[q[i]][q[i-1]]+=ret; 53 } 54 while(!q.empty()&&q.back()!=retu)//回溯到最小流量边的起点 55 { 56 vis[q.back()]=0; 57 q.pop_back(); 58 } 59 } 60 else 61 { 62 for(i=1;i<=n;i++) 63 { 64 if(g[z][i]>0&&layer[i]==layer[z]+1&&!vis[i])//必须找下层结点 65 { 66 vis[i]=1; 67 q.push_back(i); 68 break; 69 } 70 } 71 if(i>n) q.pop_back();//找不到了就回溯(这里的回溯不要更新vis,否则会错) 72 } 73 } 74 } 75 return ans;//返回的即为最大流 76 }
再回到此题。
构图:我们可以增加一个源点,源点只能提供最初的原料"0000...",故源点连边到所有接收 "0000..." 或 "若干个0及若干个2" 的机器,容量为无穷大。
再增加一个汇点,汇点能接收最终的产品"1111...",故能产出整台电脑的点,连边到汇点,容量为无穷大。
每台机器拆分成两个点,一个点接收原材料,另一个点产出原材料,他们之间连边,容量即为这台机器单位时间的产量。
最后,对于不同的机器,产出节点连边到能接受其产品的接收节点,容量无穷大。即0-0,0-2,1-1,1-2的组合可以连边,而0-1,1-0的组合不能连边。
1 #include<cstdio> 2 #include<cstring> 3 #include<deque> 4 using namespace std; 5 int m,n,g[220][220],p[110][20],w[110],vis[110],layer[110],gg[220][220]; 6 const int mmax=1<<30; 7 struct node 8 { 9 int ii,jj,ww; 10 }xie[110000]; 11 bool countlayer() 12 { 13 int i; 14 memset(layer,-1,sizeof(layer)); 15 deque<int> q; 16 layer[1]=0; 17 q.push_back(1); 18 while(!q.empty()) 19 { 20 int z=q.front(); 21 q.pop_front(); 22 for(i=1;i<=2*n+2;i++) 23 { 24 if(g[z][i]>0&&layer[i]==-1) 25 { 26 layer[i]=layer[z]+1; 27 if(i==2*n+2) return true; 28 else q.push_back(i); 29 } 30 } 31 } 32 return false; 33 } 34 int maxflow() 35 { 36 deque<int> q; 37 int i,ans=0; 38 while(countlayer()) 39 { 40 q.push_back(1); 41 memset(vis,0,sizeof(vis)); 42 vis[1]=1; 43 while(!q.empty()) 44 { 45 int z=q.back(); 46 if(z==2*n+2) 47 { 48 int ret=mmax; 49 int retu; 50 for(i=1;i<q.size();i++) 51 { 52 if(g[q[i-1]][q[i]]>0&&g[q[i-1]][q[i]]<ret) 53 { 54 ret=g[q[i-1]][q[i]]; 55 retu=i-1; 56 } 57 } 58 ans+=ret; 59 for(i=1;i<q.size();i++) 60 { 61 g[q[i-1]][q[i]]-=ret; 62 g[q[i]][q[i-1]]+=ret; 63 } 64 while(!q.empty()&&q.back()!=retu) 65 { 66 vis[q.back()]=0; 67 q.pop_back(); 68 } 69 } 70 else 71 { 72 for(i=1;i<=2*n+2;i++) 73 { 74 if(g[z][i]>0&&layer[i]==layer[z]+1&&!vis[i]) 75 { 76 vis[i]=1; 77 q.push_back(i); 78 break; 79 } 80 } 81 if(i>2*n+2) q.pop_back(); 82 } 83 } 84 } 85 return ans; 86 } 87 int main() 88 { 89 int i,j,k; 90 while(scanf("%d%d",&m,&n)!=EOF) 91 { 92 for(i=2;i<=n+1;i++) 93 { 94 scanf("%d",&w[i]); 95 for(j=1;j<=m;j++) 96 scanf("%d",&p[i][j]); 97 for(j=1;j<=m;j++) 98 scanf("%d",&p[i+n][j]); 99 } 100 memset(g,0,sizeof(g)); 101 for(i=2;i<=n+1;i++) 102 g[i][i+n]=w[i]; 103 for(i=2;i<=n+1;i++) 104 { 105 for(j=1;j<=m;j++) 106 { 107 if(p[i][j]==1) 108 break; 109 } 110 if(j>m) g[1][i]=mmax; 111 } 112 for(i=n+2;i<=2*n+1;i++) 113 { 114 for(k=1;k<=m;k++) 115 { 116 if(p[i][k]!=1) 117 break; 118 } 119 if(k>m) g[i][2*n+2]=mmax; 120 else 121 { 122 for(j=2;j<=n+1;j++) 123 { 124 for(k=1;k<=m;k++) 125 { 126 if(p[j][k]+p[i][k]==1) 127 break; 128 } 129 if(k>m) g[i][j]=mmax; 130 } 131 } 132 } 133 for(i=1;i<=2*n+2;i++) 134 { 135 for(j=1;j<=2*n+2;j++) 136 gg[i][j]=g[i][j]; 137 } 138 int aans=maxflow(); 139 int xienum=0; 140 for(i=n+2;i<=2*n+1;i++) 141 { 142 for(j=2;j<=n+1;j++) 143 { 144 if(gg[i][j]-g[i][j]>0) 145 { 146 xie[xienum].ii=i-n-1; 147 xie[xienum].jj=j-1; 148 xie[xienum++].ww=gg[i][j]-g[i][j]; 149 } 150 } 151 } 152 printf("%d %d\n",aans,xienum); 153 for(i=0;i<xienum;i++) 154 { 155 printf("%d %d %d\n",xie[i].ii,xie[i].jj,xie[i].ww); 156 } 157 } 158 return 0; 159 }