(拆点+最大流)POJ3281 Dining

时间:2023-02-01 04:30:02

题目大意是N头牛,准备了F种食物,D种饮料,每一头牛会喜欢若干种食物和饮料,但它只能选择一种食物和一种饮料,且每种食物和饮料都只够一头牛选择,问怎样分配能使得食物和饮料都能得到的牛的数量最多,求这个数。

明显的一道最大流的题目,难点只在怎么建模。建模的方法如下:

1.建立一个超级源,跟每种食物之间连一条容量为1的边;

2.建立一个超级汇,它与每种饮料之间有一条容量为1的边;

3.将每头牛都拆分成两个点C1、C2,两点之间有一条容量为1的边;

4.若一头牛喜欢食物f,就将其对应的C1点与f连接起来,容量为1,若一头牛喜欢饮料d,同理将C2与d连接起来。

为何要拆点?

不拆点不能保证牛只能选择一种食物和一种饮料这一条件。即限定牛结点的容量为1。


至于最大流方面,数据规模不大就选了EK,当然这类题目还有变种,就要考虑其余的算法及优化手段了。


代码如下:

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int map[500][500];
int pre[500];
int n,N,D,F;
queue<int>q;

bool bfs(int src,int des){
memset(pre,-1,sizeof(pre));
while (!q.empty()) q.pop();
pre[src]=-1;
int index;
q.push(src);
while(!q.empty()){
index=q.front();
q.pop();
for (int i=0;i<=1+n;i++){
if (pre[i]==-1 && map[index][i]>0){
pre[i]=index;
if (i==des)
return true;
q.push(i);
}
}
}
return false;
}

int maxflow(int src,int des){
int maxf=0;
while (bfs(src,des)){
int minflow=10000000;
for (int i=des;i!=src;i=pre[i])
minflow=min(minflow,map[pre[i]][i]);
for (int i=des;i!=src;i=pre[i]){
map[pre[i]][i]-=minflow;
map[i][pre[i]]+=minflow;
}
maxf+=minflow;
}
return maxf;
}

int main(){
int s=0,t;
scanf("%d%d%d",&N,&F,&D);
n=N*2+F+D;
memset(map,0,sizeof(map));
t=n+1;
for (int i=1;i<=F;i++)
map[s][i]=1;
for (int i=1+F+2*N;i<=D+F+2*N;i++)
map[i][t]=1;
for (int i=1+F;i<=N+F;i++)
map[i][i+N]=1;
for (int i=1;i<=N;i++){
int f,d,id;
scanf("%d%d",&f,&d);
for (int j=0;j<f;j++){
scanf("%d",&id);
map[id][i+F]=1;
}
for (int j=0;j<d;j++){
scanf("%d",&id);
map[i+F+N][id+F+2*N]=1;
}
}

printf("%d\n",maxflow(s,t));
return 0;
}