题目链接
题意
有n个工厂加工商品,商品有m个零件,工厂有最大产能q。告诉你每个工厂能接受加工成什么样子的半成品,以及经这个工厂加工之后的产品情况,问最大能生产多少商品。
思路
要拆点的。。第一次没拆也ac了,数据没卡这个。
先附POJ上一组hack数据,应输出10.
2 4
10 0 0 0 1
10 0 0 0 0
10 0 1 1 1
10 0 1 1 1
最大流题目,关键在于建图,这里先说一下建图部分
- 拆点拆成in和out两组,1-n代表in,n-2n代表out。每个工厂的in向out连边,容量为最大产能。
- 建立超级源汇,源点向所有可以从零开始加工的工厂的in连边(具体到输入数据,就是工厂输入的m个零件都为0或者2)。所有加工完毕的工厂的out(产出的m个零件都为1的)向汇点连边。容量inf或者最大产能均可
- 工厂间连边,枚举每个工厂A的out,向所有能接受这个A厂产品的工厂B的in连边(A厂输出产品m零件是0的,B厂接受的产品只能是0或2,A产出的是1的,B接受的只能是1或2,A产出的是2的,对B无要求)。容量可以inf,也可以是最大产能。
跑一遍最大流算法就可以求出答案,此外,这题还要求输出所有工厂连接,也就是最大流所有用到建图第三步的路径。我们在建图的时候都建立了反向边,反向边初始容量为0,如果反向边容量不为0了,那么说明这条边一定用到了,而且具体用了多少就是反向边的容量。遍历所有边,输出即可。
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<string>
#include<queue>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=200;
const int maxe=100010;
int ansu[maxe],ansv[maxe],answ[maxe];//最终输出
int nodev[maxn];//产能
int in_[maxn][15],out_[maxn][15];//输入输出产品标准
int head[maxn],cnt;
struct Edge{
int u;//改了下链式前向星,用于输出
int v;
int w;
int next;
}edge[maxe];
int n,m,s,t;
ll maxflow;
int deep[maxn];
int now[maxe];
void init(){
memset(head,-1,sizeof(head));
cnt=0;//我习惯从0开始建边,反向边就是1,3,5,7.。。。。
maxflow=0;
return ;
}
void add(int u,int v,int w){
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
}
inline bool bfs(){
memset(deep,0x3f,sizeof(deep));
queue<int>q;
q.push(s);deep[s] = 0;now[s] = head[s];
while(q.size()){
int x = q.front();q.pop();
for(int i=head[x];i!=-1;i=edge[i].next){
int y=edge[i].v;
if(edge[i].w>0&&deep[y]==inf){
q.push(y);
now[y]=head[y];
deep[y]=deep[x]+1;
if(y==t) return 1;
}
}
}
return 0;
}
ll dfs(int x,int flow){
if(x==t) return flow;
ll ans = 0,k,i;
for(i=now[x];i!=-1&&flow;i=edge[i].next){
now[x]=i;
int y=edge[i].v;
if(edge[i].w>0&&(deep[y]==deep[x]+1)){
k=dfs(y,min(flow,edge[i].w));
if(!k) deep[y]=inf;
edge[i].w-=k;
edge[i^1].w+=k;
ans+=k;
flow-=k;
}
}
return ans;
}
void dinic(){
while(bfs())
maxflow+=dfs(s,inf);
}
int main(){
IOS
cin>>m>>n;
s=n+n+1,t=n+n+2;
init();
for(int i=1;i<=n;i++){
cin>>nodev[i];
for(int j=1;j<=m;j++)
cin>>in_[i][j];
for(int j=1;j<=m;j++)
cin>>out_[i][j];
}
//工厂间边
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
bool ok=1;
for(int w=1;w<=m;w++){
if((out_[i][w]==1&&in_[j][w]==0)||(out_[i][w]==0&&in_[j][w]==1)){
ok=0;
break;
}
}
if(ok){
add(i+n,j,min(nodev[i],nodev[j]));
add(j,i+n,0);
}
}
}
//到这里为止,是答案需要我们输出的边,底下的不能输出
int cnt2=cnt;
//源汇连边
for(int i=1;i<=n;i++){
bool ok=1;
for(int w=1;w<=m;w++)
if(in_[i][w]==1){
ok=0;
break;
}
if(ok){
add(s,i,nodev[i]);
add(i,s,0);
}
ok=1;
for(int w=1;w<=m;w++)
if(out_[i][w]==0){
ok=0;
break;
}
if(ok){
add(i+n,t,nodev[i]);
add(t,i+n,0);
}
}
//in-out边
for(int i=1;i<=n;i++){
add(i,i+n,nodev[i]);
add(i+n,i,0);
}
dinic();
int mc=0;//边数量
//1,3,5,7,9.。。是反向边
for(int i=1;i<cnt2;i+=2){
if(edge[i].w){
ansu[mc]=edge[i].v-n;
ansv[mc]=edge[i].u;
answ[mc]=edge[i].w;
mc++;
}
}
cout<<maxflow<<" "<<mc<<endl;
for(int i=0;i<mc;i++)
cout<<ansu[i]<<" "<<ansv[i]<<" "<<answ[i]<<endl;
return 0;
}