题意(参考北京大学郭炜老师课件):每个工厂有三个动作:1)接收原材料;2)生产 ;3)将其产出的半成品给其他机器,或产出成品。这三个过程都对应不同的流量。对第一组输入数据的分析:
输入:
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
输出:
25 2
1 3 15
2 3 10
输入解释:电脑由3个部件组成,共有4台机器,1号机器产量15, 能给空电脑加上2号部件;2号机器能给空电脑加上2号部件和3号部件,;3号机器能把具有1个2号部件和3号部件有无均可的电脑变成成品;4号机器能把具有1个3号部件和2号部件有无均可的电脑变成成品.
输出:单位时间最大产量25,有两台机器有协作关系:
1号机器单位时间内要将15个电脑给3号机器加工
2号机器单位时间内要将10个电脑给3号机器加工
注意:输出可能由多种可能,输出其中任意一种即可,而且协作关系的顺序随意。所以对于上述输入,如下输出也是正确的:
25 3
2 4 3
2 3 7
1 3 15
思路:
1) 添加一个原点S,S提供最初的原料 00000...
2) 添加一个汇点T, T接受最终的产品 11111....
3) 将每个机器拆成两个点: 编号为i的接收节点,和编号为i+n的产出节点(n是机器数目),前者用于接收原料,后者用于提供加工后的半成品或成品。这两个点之间要连一条边,容量为单位时间产量Qi
4) S 连边到所有接收 "0000..." 或 "若干个0及若干个2" 的机器,容量为无穷大
5) 产出节点连边到能接受其产品的接收节点,容量无穷大
6) 能产出成品的节点,连边到T,容量无穷大。
7) 求S到T的最大流
#include <stdio.h>
#include <string.h>
#define min(a,b) a<b?a:b
#define N 105
#define INF 0x3fffffff
struct edge{
int y,c,next;
}e[N*N];
int p,n,top;
int in[N/2][11],out[N/2][11],w[N/2];
int index[N],first[N],pre[N],a[N],q[200000];
int iszero(int x[11]){//可以接受空电脑,也就是连接到超级源点
int i,temp=0;
for(i = 1;i<=p;i++)
if(x[i] == 1)
return 0;
return 1;
}
int isone(int x[11]){//所有的零件装配齐全,连接到超级汇点
int i,temp=0;
for(i = 1;i<=p;i++)
temp += x[i];
if(temp==p)
return 1;
return 0;
}
void add(int x,int y,int c){
e[top].y = y;
e[top].c = c;
e[top].next = first[x];
first[x] = top++;
e[top].y = x;
e[top].c = 0;
e[top].next = first[y];
first[y] = top++;
}
int test(int x[11],int y[11]){//测试y能否接收x的输出
int i;
for(i = 1;i<=p;i++)
if(x[i]+y[i] == 1)
return 0;
return 1;
}
void maxflow(int s,int t){
int i,j,res=0,sum=0,now,front,rear;
front = rear = -1;
while(1){
memset(a,0,sizeof(a));
memset(pre,0,sizeof(pre));
q[++rear] = s;
a[s] = INF;
while(front < rear){
now = q[++front];
for(i = first[now];i!=-1;i=e[i].next)
if(!a[e[i].y] && e[i].c>0){
q[++rear] = e[i].y;
a[e[i].y] = min(a[now],e[i].c);
pre[e[i].y] = now;
index[e[i].y] = i;
}
}
if(!a[t])
break;
res += a[t];
for(i = t;i!=0;i=pre[i]){
e[index[i]].c -= a[t];
e[index[i]^1].c += a[t];
}
}
for(i = t-2;i>0;i-=2)//计算有协作关系的数量
for(j = first[i];j!=-1;j=e[j].next)
if(e[j].c>0 && e[j].y!=0 && e[j].y!=i+1)//奇数节点的反向边有流量且不是留回源点的就是我们要找的
sum++;
printf("%d %d\n",res,sum);
}
void print(int t){
int i,j;
for(i = t-2;i>0;i-=2)
for(j = first[i];j!=-1;j=e[j].next)
if(e[j].c>0 && e[j].y!=0 && e[j].y!=i+1)
printf("%d %d %d\n",e[j].y/2,(i+1)/2,e[j].c);//奇数节点的反向边有流量且不是留回源点的就是我们要找的
}
int main(){
freopen("a.txt","r",stdin);
while(scanf("%d %d",&p,&n)!=EOF){
int i,j;
top = 0;
memset(first,-1,sizeof(first));
for(i = 1;i<=n;i++){
scanf("%d",&w[i]);
for(j = 1;j<=p;j++)
scanf("%d",&in[i][j]);
for(j = 1;j<=p;j++)
scanf("%d",&out[i][j]);
if(iszero(in[i]))
add(0,2*i-1,INF);
if(isone(out[i]))
add(2*i,2*n+1,INF);
add(2*i-1,2*i,w[i]);
}
for(i = 1;i<=n;i++)
for(j = 1;j<=n;j++)
if(test(out[i],in[j]))
add(2*i,2*j-1,INF);
maxflow(0,2*n+1);
print(2*n+1);
}
return 0;
}