HDU4292-FOOD-最大流-拆点

(有任何问题欢迎留言或私聊

题目:hdu4292传送门

 题目大意:

 有n个人,num1种食物,num2种饮料。每种食物和每种饮料是有限的。每个人有自己喜欢的食物和饮料的种类。问最多有多少人选到自己喜欢的食物和饮料的搭配方案。
 每个人只能选一个食物和一杯饮料

思路:

 这是一道最大流专题里的题目。拿到题很容易想到这一种建图方式。

超级源点S向每种食物连边,流量为该种食物的数量。
每种饮料向超级汇点T连边,流量为该种饮料的数量。
每个人向自己喜欢的饮料和食物连边,流量为1。

 一开始肯定会有这种建图的想法。但是这样还不够。

 题目说了,每个人只能选一杯饮料和一个食物。这样建图很明显不能保证每个人只选了一杯饮料和一个食物。还有问题。

举个栗子:

只有一个人,他喜欢3种食物,3种饮料。每种食物和饮料的数量为1.

 把这个图跑一边最大流你会发现你得到的答案是3,而不是1!

为什么呢?因为你没处理每个人只能选一个食物和饮料的限制条件。

解决方法:

把每个人拆成两个点,不妨称作左点 i 和右点i + n。左点和右点连边,流量设为1.
重新建图:
每种食物向喜欢它的人的左点 i 连边,流量为1.
每个人的右点 i + n 向他喜欢的饮料连边,流量为1.
然后跑最大流就okey了。

这样新图就建好了。这就是网络流中的一种常用技巧:拆点
处理有限制条件的图的时候经常使用 !

AC代码:

//c++ 202ms
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const int MX=805;
char ar[MX];
int s,t,e,fr[MX],h[MX],nh[MX];
LL ans;
int n,num1,num2;
const LL inf=1e17;
inline int rd(){
    int x=0;
    char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x;
}
struct lp{
    int t,ne;
    LL w;
}a[MX*1000];
void ins(int f,int t,LL w){
    a[++e].t=t,a[e].ne=fr[f],fr[f]=e,a[e].w=w;
    a[++e].t=f,a[e].ne=fr[t],fr[t]=e,a[e].w=0;
}
LL Min(LL x,LL y){
    return x<y?x:y;
}
LL sap(int u,LL fl){
    if (u==t) return fl;
    LL res=fl;
    for (int i=fr[u];i;i=a[i].ne){
        if (a[i].w&&h[a[i].t]+1==h[u]){
            LL t=sap(a[i].t,Min(res,a[i].w));
            a[i].w-=t,a[i^1].w+=t;
            if (!(res-=t)) return fl;
        }
    }
    if (!(--nh[h[u]])) h[t]=t;
    ++nh[++h[u]];
    return fl-res;
}
int main(){
    while(~scanf("%d%d%d",&n,&num1,&num2)){
        memset(fr,0,sizeof(fr));
        memset(nh,0,sizeof(nh));
        memset(h,0,sizeof(h));
        s=1,t=2*n+num1+num2+2;ans=0;e=1;
        int x;
        for(int i=0;i<n;++i)ins(i+2,i+2+n,1);
        for(int i=0;i<num1;++i){
            scanf("%d",&x);
            ins(s,2*n+i+2,x);
        }
        for(int i=0;i<num2;++i){
            scanf("%d",&x);
            ins(2*n+i+2+num1,t,x);
        }
        for(int i=0;i<n;++i){
            scanf("%s",ar);
            for(int j=0;j<num1;++j){
                if(ar[j]=='Y')ins(2*n+j+2,i+2,1);
            }
        }
        for(int i=0;i<n;++i){
            scanf("%s",ar);
            for(int j=0;j<num2;++j){
                if(ar[j]=='Y')ins(i+2+n,2*n+j+2+num1,1);
            }
        }
        nh[0]=t;
        while (h[t]!=t){ 
            ans+=sap(s,inf);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

OvO

猜你喜欢

转载自blog.csdn.net/qq_39599067/article/details/80599415