题目链接:
题意:
每台ACM 计算机包含P 个部件,当所有这些部件都准备齐全后,计算机就可以组装了,组装好以后就可以交给竞赛队伍使用了。计算机的生产过程是全自动的,通过N 台不同的机器来完成。每台机器从一台半成品计算机中去掉一些部件,并加入一些新的部件(去除一些部件在有的时候是必须的,因为计算机的部件不能以任意的顺序组装)。每台机器用它的性能(每小时组装多少台计算机)、输入/输出规格来描述。
输入规格描述了机器在组装计算机时哪些部件必须准备好了。输入规格是由P 个整数组成,每个整数代表一个部件,这些整数取值为0, 1 或2,其中0 表示该部件不应该已经准备好了,1表示该部件必须已经准备好了,2 表示该部件是否已经准备好了无关紧要。
输出规格描述了该机器组装的结果。输出规格也是由P 个整数组成,每个整数取值为0 或1,其中0 代表该部件没有生产好,1 代表该部件生产好了。
机器之间用传输速度非常快的流水线连接,部件在机器之间传送所需的时间与机器生产时间相比是十分小的。
给出上述关于n台机器的描述,求一小时最多组成多少台计算机,并输出流水线的路径
解题思路:
题目已给出条件: 机器之间传输不需要时间,也就可以理解为流水线上每台机器的工作都是同步的,这样就可以用网络流来解决了(单位时间问题)
解题的关键在于建图.
话不多说,给出一张样例图:
图中红色边的容量应设为无穷大(单位时间运输无限台计算机)
值得注意的是 图中两台机器的连接可以是双向的(比如可以同时又B1->A2 ,B2->A1 )
关于路径的输出(找从n+1~2*n)开始的正向边,有流则表示生产线经过这条路径
代码:
#include <iostream>
#include <cstring>
#include<cstdio>
#define LL long long
#include <queue>
const int MAXN =205;
const int MAXM=440020;
const int INF=0x3f3f3f3f;
using namespace std;
struct Edge
{
int from;
int to,cap,flow,next;
} edge[MAXM];
int head[MAXN],tot,gap[MAXN],d[MAXN],cur[MAXN],que[MAXN],p[MAXN];
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int c,int f)
{
edge[tot]=(Edge)
{
u,v,c,f,head[u]
};
head[u] = tot++;
edge[tot]=(Edge)
{
v,u,c,c,head[v]
};
head[v] = tot++;
}
int isap(int source,int sink,int N)
{
memset(gap,0,sizeof(gap));
memset(d,0,sizeof(d));
memcpy(cur,head,sizeof(head));
int top = 0,x = source,flow = 0;
while(d[source] < N)
{
if(x == sink)
{
int Min = INF,inser=0;
for(int i = 0; i < top; ++i)
{
if(Min > edge[p[i]].cap - edge[p[i]].flow)
{
Min = edge[p[i]].cap - edge[p[i]].flow;
inser = i;
}
}
for(int i = 0; i < top; ++i)
{
edge[p[i]].flow += Min;
edge[p[i]^1].flow -= Min;
}
if(Min!=INF) flow += Min;
top = inser;
x = edge[p[top]^1].to;
continue;
}
int ok = 0;
for(int i = cur[x]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow && d[v]+1 == d[x])
{
ok = 1;
cur[x] = i;
p[top++] = i;
x = edge[i].to;
break;
}
}
if(!ok)
{
int Min = N;
for(int i = head[x]; i != -1; i = edge[i].next)
{
if(edge[i].cap > edge[i].flow && d[edge[i].to] < Min)
{
Min = d[edge[i].to];
cur[x] = i;
}
}
if(--gap[d[x]] == 0) break;
gap[d[x] = Min+1]++;
if(x != source) x = edge[p[--top]^1].to;
}
}
return flow;
}
int state[MAXN][2][10];
int pp,n;
int equall(int x,int y)
{
for(int i=0;i<pp;i++)
if(state[y][0][i]+state[x][1][i]==1)
return 0;
return 1;
}
int main()
{
// freopen("in.txt","r",stdin);
int w,flag1,flag2;
while(~scanf("%d%d",&pp,&n))
{
init();
memset(state,0,sizeof(state));
for(int i=1;i<=n;i++)
{
flag1=flag2=1;
scanf("%d",&w);
for(int j=0;j<pp;j++){ //全是0或2 则连接源点
scanf("%d",&state[i][0][j]);
if(state[i][0][j]==1)
flag1=0;
}
for(int j=0;j<pp;j++){ //全是1 则连接汇点
scanf("%d",&state[i][1][j]);
if(state[i][1][j]==0||state[i][1][j]==2)
flag2=0;
}
addedge(i,i+n,w,0); //拆点连自己
if(flag1==1)
addedge(0,i,INF,0);
if(flag2==1)
addedge(i+n,2*n+1,INF,0);
for(int j=1;j<i;j++){
if(equall(i,j))
addedge(n+i,j,INF,0);
if(equall(j,i)) //注意不能是else if 两个机器可以是双向连接的
addedge(n+j,i,INF,0);
}
}
printf("%d",isap(0,2*n+1,2*n+2));
int ans[400][3];
int ss=0;
for(int i=n+1;i<=(n<<1);i++) //邻接表找最大流中的边
for(int j=head[i];j!=-1;j=edge[j].next)
{
if(edge[j].to==2*n+1||edge[j].to==i-n)
continue;
if(edge[j].flow>0){
ans[ss][0]=edge[j].from-n;
ans[ss][1]=edge[j].to;
ans[ss++][2]=edge[j].flow;
}
}
printf(" %d\n",ss);
for(int i=0;i<ss;i++)
printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]);
}
return 0;
}