题意:一家电脑厂里,有N台组装电脑的机器,电脑由P个零件组成,每台机器有in[P]和out[P]两个参数,表示该机器需要in[P]所指示的零件,产出out[P]所指示的零件,同时每台机器每小时最多产出Q[i]组out[P]零件。问如何连接各个机器,使得每小时产出的电脑数量最多,同时输出连接的次数与方式。
这道题是网络流最大流问题,但是有一点小变化。在最基本的最大流问题中,边是带固定容量的,但在本题中,边并没有固定容量,反而是顶点带有容量,边的容量是不定的,可以分配的。因此需要对最大流算法做一点小小的修改才可。
此外,由于这道题本身没有源点和汇点,因此我们可以在网络图中增添源点s=1,汇点t=n+2,其他顶点编号为2-n+1,同时,源点s有对应的out[1…P]=0,没有in[P],汇点t有对应的in[1…P]=1,没有out[P]。
代码如下:
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<cstring>
#include<stack>
using namespace std;
const int maxn=55;
const int maxm=3e3+5;
const int inf=0x7f7f7f7f;
typedef long long ll;
int w[maxn],in[maxn][11],out[maxn][11];
int head[maxn],cnt;
struct Edge
{
int u,v,cap,next; //u为起点,v为终点,cap为该边所连起点的容量,next为起点相同的下一边
}e[maxm];
struct Dinic //最大流Dinic算法,封装在结构体中
{
int s,t,num; //源点,汇点
int dis[maxn],cur[maxn]; //dis[]用于构造层次网络,表示顶点i到源点s的距离或相对s的层次
stack<int> S; //用于保存,输出路径
bool bfs() //BFS构造层次网络
{
memset(dis,0,sizeof(dis));
queue<int> Q;
Q.push(s);
dis[s]=1;
while(!Q.empty())
{
int fro=Q.front(); Q.pop();
for(int i=head[fro]; i ;i=e[i].next)
{
if(e[i].cap&&!dis[e[i].v])
{
Q.push(e[i].v);
dis[e[i].v]=dis[fro]+1;
}
}
}
return dis[t]!=0; //dis[t]=0说明此时已经没有从源点到汇点的路径,即不存在增广路了,整个Dinic算法可以结束
}
void proc(int pos,int flow) //找到增广路中的最小残流后,调用此函数,将起点为pos的边的容量都减去flow
{
for(int i=head[pos]; i ;i=e[i].next)
e[i].cap-=flow;
}
void copy_track(stack<int> st,int temp) //复制路径
{
st.pop(); st.pop(); //与s,t相连的边都不要,这里先弹出与t相连的边
while(!st.empty())
{
int u,v;
u=st.top(); st.pop();
v=st.top(); st.pop();
if(u!=s)
{
num++;
S.push(temp);
S.push(v-1);
S.push(u-1);
}
}
}
int dfs(int pos,int flow,stack<int> st) //DFS在层次图中多次增广
{
if(pos==t) return flow;
int ret=0;
for(int i=cur[pos]; flow&&i ;i=e[i].next)
{
cur[pos]=i;
if(e[i].cap&&dis[e[i].v]==dis[pos]+1)
{
st.push(e[i].v); st.push(e[i].u); //一定要先于下一步深搜之前保存路径
int temp=dfs(e[i].v,min(e[i].cap,flow),st);
if(temp>0)
{
proc(e[i].u,temp);
flow-=temp; //多次增广的关键
if(e[i].v==t)
copy_track(st,temp); //复制路径
ret+=temp;
}
else dis[e[i].v]=-1;
st.pop(); st.pop(); //记得弹出
}
}
return ret;
}
void maxflow(int n)
{
int ans=0;
while(bfs())
{
stack<int> st;
for(int i=1;i<=n;i++) cur[i]=head[i];
ans+=dfs(s,inf,st);
}
printf("%d %d\n",ans,num);
while(!S.empty())
{
int u,v,w;
u=S.top(); S.pop();
v=S.top(); S.pop();
w=S.top(); S.pop();
printf("%d %d %d\n",u,v,w);
}
}
}dinic;
void add(int u,int v,int w) //添加边
{
e[cnt].u=u; e[cnt].v=v; e[cnt].cap=w; e[cnt].next=head[u]; head[u]=cnt++;
e[cnt].u=v; e[cnt].v=u; e[cnt].cap=0; e[cnt].next=head[v]; head[v]=cnt++; //反向边
}
void init(int n,int p) //初始化数据并建立网络图
{
memset(head,0,sizeof(head));
cnt=2; dinic.num=0;
for(int i=1;i<=n+1;i++)
{
for(int j=2;j<=n+2;j++)
{
if(j==i) continue;
int flag=1;
for(int k=1;k<=p;k++)
{
if(out[i][k]!=in[j][k])
{
if(in[j][k]!=2)
{
flag=0;
break;
}
}
}
if(flag)
add(i,j,w[i]);
}
}
}
int main()
{
int n,p;
while(~scanf("%d%d",&p,&n))
{
for(int i=1;i<=p;i++)
{
out[1][i]=0;
in[n+2][i]=1;
}
w[1]=inf; //这一步要记得
for(int i=2;i<=n+1;i++)
{
scanf("%d",&w[i]);
for(int j=1;j<=p;j++)
scanf("%d",&in[i][j]);
for(int j=1;j<=p;j++)
scanf("%d",&out[i][j]);
}
init(n,p);
dinic.s=1; dinic.t=n+2;
dinic.maxflow(n+2);
}
return 0;
}