题意
N台机器生产P个产品,流水线作业。每台机器可以同时执行W个相同任务,每个任务将P个产品的状态从P个状态变换为另外P个状态。求最多可以同时生产多少个产品?
题解
WA了好多次。。。不过讲道理这道题的核心在于点拆分,至于Dinic算法,直接把《训练指南》那本书上的Dinic代码搞上去就可以了。一旦涉及到这种流水线作业,肯定是要有一个超级源点和超级终点,这个在《挑战程序设计竞赛》上讲的很详细,这里就不解释了。至于点拆分,很巧妙,将点与其他点的流限制设为INF,将点的起点和终点拆分,流限制为W。然后再套用Dinic,很轻松就能求出来最大流。
注意事项
没什么好注意的,最重要的是Dinic算法一定要敲对,在这上面WA了好几发。。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<iostream>
#include<queue>
#define MAXN 120
#define INF 1e9
using namespace std;
int p,n;
bool isStart(int x[]){
for(int i=0;i<p;i++){
if(x[i]==1)
return false;
}
return true;
}
bool isEnd(int x[]){
for(int i=0;i<p;i++){
if(x[i]==0)
return false;
}
return true;
}
bool isInAndOut(int in[],int out[]){
for(int i=0;i<p;i++){
if(out[i]!=in[i]&&in[i]!=2){
return false;
}
}
return true;
}
struct Edge{
int from,to,cap,flow;
};
struct Dinic{
int n,m,s,t;
vector<Edge> edges;
vector<int> g[MAXN];
bool vis[MAXN];
int d[MAXN];
int cur[MAXN];
void init(int n){
this->n=n;
edges.clear();
for(int i=0;i<=n;i++)
g[i].clear();
}
void addEdge(int from,int to,int cap){
edges.push_back((Edge){from,to,cap,0});
edges.push_back((Edge){to,from,0,0});
m=edges.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
bool bfs(){
memset(vis,0,sizeof(vis));
memset(d,-1,sizeof(d));
queue<int> q;
q.push(s);
d[s]=0;
vis[s]=true;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<g[x].size();i++){
Edge e=edges[g[x][i]];
if(!vis[e.to]&&e.cap>e.flow){
vis[e.to]=true;
d[e.to]=d[x]+1;
q.push(e.to);
}
}
}
return vis[t];
}
int dfs(int x,int a){
if(x==t||a==0)
return a;
int flow=0,f;
for(int& i=cur[x];i<g[x].size();i++){
Edge& e=edges[g[x][i]];
if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0){
e.flow+=f;
edges[g[x][i]^1].flow-=f;
flow+=f;
a-=f;
if(a==0)
break;
}
}
return flow;
}
int maxFlow(int s,int t){
this->s=s,this->t=t;
int flow=0;
while(bfs()){
memset(cur,0,sizeof(cur));
flow+=dfs(s,INF);
}
return flow;
}
};
int main()
{
int w[60],in[60][12],out[60][12];
while(~scanf("%d%d",&p,&n)){
Dinic di;
int s=0,t=2*n+1;
di.init(t);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
for(int j=0;j<p;j++){
scanf("%d",&in[i][j]);
}
for(int j=0;j<p;j++){
scanf("%d",&out[i][j]);
}
if(isStart(in[i])){
di.addEdge(s,i,INF);
}
if(isEnd(out[i])){
di.addEdge(i+n,t,INF);
}
}
for(int i=1;i<=n;i++){
di.addEdge(i,i+n,w[i]);
for(int j=1;j<=n;j++){
if(i==j)
continue;
if(isInAndOut(in[j],out[i])){
di.addEdge(i+n,j,INF);
}
}
}
int flow=di.maxFlow(s,t);
int cnt=0;
for(int i=0;i<di.edges.size();i++){
if(di.edges[i].from==s||di.edges[i].to==s||di.edges[i].from==t||di.edges[i].to==t)
continue;
if((di.edges[i].from+n)==di.edges[i].to||(di.edges[i].from-n)==di.edges[i].to)
continue;
if(di.edges[i].flow<0)
cnt++;
}
printf("%d %d\n",flow,cnt);
for(int i=0;i<di.edges.size();i++){
if(di.edges[i].from==0||di.edges[i].to==0||di.edges[i].from==2*n+1||di.edges[i].to==2*n+1)
continue;
if((di.edges[i].from+n)==di.edges[i].to||(di.edges[i].from-n)==di.edges[i].to)
continue;
if(di.edges[i].flow<0)
printf("%d %d %d\n",di.edges[i].to-n,di.edges[i].from,-di.edges[i].flow);
}
}
return 0;
}