您的位置:首页 > 其它

POJ3281 Dining (拆点+最大流)

2016-09-04 19:21 381 查看
题意:

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

思路:

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

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

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

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

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

为何要拆点?

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

我的代码里1到f为食物点,f+1到f+2*n为牛点,f+2*n+1到f+2*n+d为饮料点

#include<cstdio>
#include<cstring>
using  namespace std;

const int maxn=500;//点数的最大值
const int maxm=250000;//边数的最大值
const int inf=0x3f3f3f3f;
struct Edge {
int to,next,cap,flow;
}edge[maxm];//注意是maxm
int tol;
int head[maxn];
int cur[maxn],d[maxn];// 当前弧下标   结点到汇点距离下界
int p[maxn],gap[maxn];//可增广路上的上一条弧   gap优化  //比dinic多的两个数组

void init() {
tol=0;
memset(head,-1,sizeof(head));
}

//加边,单向图三个参数,双向图四个参数
void addedge(int u,int v,int w,int rw=0) {
edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u];
edge[tol].flow=0; head[u]=tol++;
edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v];
edge[tol].flow=0; head[v]=tol++;
}

//输入参数:起点、终点、点的总数
//点的编号没有影响,只要输入点的总数
int sap(int s,int t,int N){
memset(gap, 0, sizeof(gap));
memset(d, 0, sizeof(d));
memcpy(cur, head, sizeof(head));
int u=s;
p[u]=-1;
gap[0]=N;
int ans=0;
while(d[s]<N){
if(u == t){
int Min=inf;
for(int i=p[u]; i!=-1; i=p[edge[i^1].to])//找最小残量值
if(Min>edge[i].cap-edge[i].flow)
Min=edge[i].cap-edge[i].flow;
for(int i = p[u]; i!=-1; i=p[edge[i^1].to]){//增广
edge[i].flow+=Min;
edge[i^1].flow-=Min;
}
u=s;
ans+=Min;
continue;
}
bool ok=false;
int v;
for(int i=cur[u]; i!=-1; i=edge[i].next){
v=edge[i].to;
if(edge[i].cap-edge[i].flow && d[v]+1==d[u]){//Advance前进
ok=true;
cur[u]=p[v]=i;
break;
}
}
if(ok){
u=v;
continue;
}
//Retreat走不动了,撤退
int Min=N;
for(int i=head[u]; i!=-1; i=edge[i].next)
if(edge[i].cap-edge[i].flow && d[edge[i].to] < Min){
Min=d[edge[i].to];
cur[u]=i;
}
gap[d[u]]--;
if(!gap[d[u]])return ans;
d[u] = Min+1;
gap[d[u]]++;
if(u!=s) u=edge[p[u]^1].to;//退一步,沿父边返回
}
return ans;
}

int main(){
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif

int f,d,n;
int fnum,dnum,tmp,source,sink;
while(~scanf("%d%d%d",&n,&f,&d)){
init();
source=0;sink=2*n+f+d+1;
for(int i=1; i<=f; i++)addedge(source, i, 1);//源点指向食物
for(int i=1; i<=d; i++)addedge(f+2*n+i, sink, 1);//饮料指向汇点
for(int i=1; i<=n; i++)addedge(f+i, f+n+i, 1);//牛指向牛

for(int i=1; i<=n ;i++){
scanf("%d%d",&fnum,&dnum);
while(fnum--){
scanf("%d",&tmp);
addedge(tmp, f+i, 1);//食物指向牛
}
while(dnum--){
scanf("%d",&tmp);
addedge(f+n+i, f+2*n+tmp, 1); //牛指向饮料
}
}
printf("%d\n",sap(source, sink, f+2*n+d+2));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  最大流