[最大流]BZOJ 1711: [Usaco2007 Open]Dining吃饭 题解

来源:互联网 发布:主人网络关闭了怎么办 编辑:程序博客网 时间:2024/05/18 01:00

本题有权限……

题目描述

农夫JOHN为牛们做了很好的食品,但是牛吃饭很挑食. 每一头牛只喜欢吃一些食品和饮料而别的一概不吃.虽然他不一定能把所有牛喂饱,他还是想让尽可能多的牛吃到他们喜欢的食品和饮料. 农夫JOHN做了F (1 <= F <= 100) 种食品并准备了D (1 <= D <= 100) 种饮料. 他的N (1 <= N <= 100)头牛都以决定了是否愿意吃某种食物和喝某种饮料. 农夫JOHN想给每一头牛一种食品和一种饮料,使得尽可能多的牛得到喜欢的食物和饮料. 每一件食物和饮料只能由一头牛来用. 例如如果食物2被一头牛吃掉了,没有别的牛能吃食物2.

输入格式

  • 第一行: 三个数: N, F, 和 D

  • 第2..N+1行: 每一行由两个数开始F_i 和 D_i, 分别是第i 头牛可以吃的食品数和可以喝的饮料数.下F_i个整数是第i头牛可以吃的食品号,再下面的D_i个整数是第i头牛可以喝的饮料号码.

输出格式

  • 第一行: 一个整数,最多可以喂饱的牛数.

输入样例

4 3 3
2 2 1 2 3 1
2 2 2 3 1 2
2 2 1 3 1 2
2 1 1 3 3

输出样例

3

样例解释:

牛 1: 食品从 {1,2}, 饮料从 {1,2} 中选
牛 2: 食品从 {2,3}, 饮料从 {1,2} 中选
牛 3: 食品从 {1,3}, 饮料从 {1,2} 中选
牛 4: 食品从 {1,3}, 饮料从 {3} 中选

一个方案是:
Cow 1: 不吃
Cow 2: 食品 #2, 饮料 #2
Cow 3: 食品 #1, 饮料 #1
Cow 4: 食品 #3, 饮料 #3

用鸽笼定理可以推出没有更好的解 (一共只有3总食品和饮料).当然,别的数据会更难.

解题分析

这难道是传说中的三分图匹配?算了,我也不会……

可不可以转化成两个独立的二分图匹配?也不行,自己手造一个数据分分钟卡掉。

那么还要怎么做?对于二分图匹配,我们有用最大流的方法来解决,那么这道题应该也可以对题目进行网络流建模再解决,事实上也正是如此。

所以这道题的关键就成了建模……

初始想法是先一列牛再一列食物再一列饮料,但是食物和饮料如何连边?然后就是一个神奇的思路——把牛放中间,这样的话建边就很容易……吗?

这样建图是有问题滴!因为这样一头牛有可能有多个食物的流量进入这个“牛”点,然后又流向多个饮料点最后流到汇点,但这样违反了一头牛只能选一种食物和一种饮料的规则,所以解决方案就是——拆点(这是网络流常用套路之一)。

对于每头牛拆成两个点i1和i2,并额外加一个源点和一个汇点(同样是网络流常用套路之一)那么首先源点向每个食物建一条边,然后食物向想要吃它的牛对应的点i1建一条边,然后i1向i2建一条边,然后i2向想要喝的饮料连一条边,最后所有饮料向汇点连一条边(不得不说太神奇了),所有边的流量限制均为1。

然后Dinic刷最大流就行了,EK貌似不行,e有40000+,EK难以承受,其实DinicO(n2m)的算法对于e40000+,n400的大小貌似也不行,但是貌似这种所有边限制均为1的图Dinic复杂度会低很多,蓝书上好像有,我不记得了……

复杂度

时间:O(n2m)(具体复杂度查蓝书,这是正常情况下Dinic复杂度)
空间:O(e)

#include<cstdio>#include<cstring>#include<algorithm>#define maxn 405#define maxe 50005using namespace std;const int INF=(((1<<30)-1)<<1)+1;int n,m,k,s,t,tot,lnk[maxn],nxt[maxe],son[maxe],que[maxn],dst[maxn],cap[maxe],flow[maxe],lst[maxn];bool vs[maxn];inline void readi(int &x){    x=0; char ch=getchar();    while ('0'>ch||ch>'9') ch=getchar();    while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}}void _add(int x,int y,int z){    tot++; son[tot]=y; nxt[tot]=lnk[x]; cap[tot]=z; flow[tot]=0; lnk[x]=tot;    tot++; son[tot]=x; nxt[tot]=lnk[y]; cap[tot]=0; flow[tot]=0; lnk[y]=tot;}void _init(){    freopen("dingin.in","r",stdin);    freopen("dingin.out","w",stdout);    memset(lnk,0,sizeof(lnk));    memset(nxt,0,sizeof(nxt));    readi(n); readi(m); readi(k); s=2*n+m+k+1; t=s+1; tot=1;    for (int i=1;i<=n;i++) _add(i,i+n,1);    for (int i=1;i<=m;i++) _add(s,2*n+i,1);    for (int i=1;i<=k;i++) _add(2*n+m+i,t,1);    for (int i=1,ka,kb,x;i<=n;i++){        readi(ka); readi(kb);        for (int j=1;j<=ka;j++) {readi(x); _add(2*n+x,i,1);}        for (int j=1;j<=kb;j++) {readi(x); _add(i+n,2*n+m+x,1);}    }}bool _bfs(){        memset(vs,0,sizeof(vs));    memset(dst,0,sizeof(dst));    int hed=0,til=1; que[1]=s; vs[s]=1; dst[s]=0;    while (hed!=til){        int x=que[++hed];        for (int j=lnk[x];j;j=nxt[j])            if (!vs[son[j]]&&cap[j]>flow[j]) {que[++til]=son[j]; dst[son[j]]=dst[x]+1; vs[son[j]]=1;}    }    return vs[t];}int _dfs(int x,int now){    if (x==t||now==0) return now; int tem=0;    for (int j=lst[x];j;lst[x]=j=nxt[j])        if (dst[x]+1==dst[son[j]]){            int ew=_dfs(son[j],min(now,cap[j]-flow[j]));            if (ew){                tem+=ew; now-=ew; flow[j]+=ew; flow[j^1]-=ew;                if (!now) break;            }        }    return tem;}int FDinic(){    int ans=0;    while (_bfs()){        for (int i=1;i<=t;i++) lst[i]=lnk[i];        ans+=_dfs(s,INF);    }    return ans;}void _solve(){    int ans=FDinic();    printf("%d",ans);}int main(){    _init();    _solve();    return 0;}
原创粉丝点击