poj3281 Dining 详解(最大流)

问题描述

牛是很挑食的。每头牛都偏爱特定的食物和饮料,其他的她都不吃。
农夫约翰为他的牛做了美味的饭菜,但他忘了根据它们的喜好检查菜单。虽然他可能不能填饱所有的人,但他想给尽可能多的奶牛提供一顿完整的食物和饮料。
农民John煮了F(1≤F≤100)种食物,准备了D(1≤D≤100)种饮料。他的每头牛(1≤N≤100)已经决定了她是吃某种特定的食物还是喝某种特定的饮料。农民约翰必须为每头奶牛分配一种食物和一种饮料,以使同时获得这两种食物和饮料的奶牛数量最大化。
每一道菜或饮料只能由一头奶牛食用(即,一旦将食物类型2分配给一头奶牛,其他奶牛就不能被分配食物类型2)。

输入

第1行:三个空格分隔的整数:N、F和D
行2 . .N+1:每一行i以两个整数Fi和Di开始,分别是我喜欢的菜的数量和我喜欢的饮料的数量。下一个Fi整数表示我要吃的牛的盘子,后面的Di整数表示我要喝的牛的饮料。

输出

第1行:一个整数,它是可以同时喂养符合奶牛意愿的食物和饮料的最大奶牛数量

分析:

把每个牛拆成两个牛
建立一个
源点->食物->牛->牛->饮料->汇点(两个牛相同)
的图,边权都为1
最大流就是最大匹配数

为什么是 “食物->牛->牛->饮料”而不是“食物->牛->饮料”呢?
因为如果是
“食物->牛->饮料”

那么会出现这种情况:
在这里插入图片描述
意思是不能保证一头牛只选择一个食物和饮料

而 如果是“食物->牛->牛->饮料”的话:
在这里插入图片描述
(画的真烂)
由于有中间黑色箭头指向的那条路的流量限制,所以可以保证只一头牛只选择一个食物和饮料

总图差不多是这样的:
在这里插入图片描述

code(EK算法实现):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<algorithm>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=1e3+5;;
int g[maxm][maxm];
int n,f,d;
int mark[maxm];
int pre[maxm];
int all;
int bfs(int s,int t){
    memset(mark,0,sizeof mark);
    memset(pre,0,sizeof pre);
    queue<int>q;
    q.push(s);
    mark[s]=1;
    pre[s]=s;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=0;i<=all;i++){
            if(!mark[i]&&g[x][i]){
                pre[i]=x;
                if(i==t)return 1;
                mark[i]=1;
                q.push(i);
            }
        }
    }
    return 0;
}
int ek(int s,int t){
    int ans=0;
    while(bfs(s,t)){
        int d=inf;
        for(int i=t;i!=s;i=pre[i]){//找最小detla
            d=min(d,g[pre[i]][i]);
        }
        for(int i=t;i!=s;i=pre[i]){
            g[pre[i]][i]-=d;//正边减小
            g[i][pre[i]]+=d;//反边增加
        }
        ans+=d;
    }
    return ans;
}
int main(){
    while(cin>>n>>f>>d){
        all=f+n+n+d+1;//总点数
        memset(g,0,sizeof g);
        for(int i=1;i<=f;i++){//食物是1-f
            g[0][i]=1;//源点和食物建边
        }
        for(int i=1;i<=n;i++){//牛是(f+1)-(f+n)  和(f+n+1)-(f+n+n);
            int k1,k2;
            cin>>k1>>k2;
            while(k1--){
                int t;
                cin>>t;
                g[t][f+i]=1;//食物和牛建边
            }
            g[f+i][f+n+i]=1;//牛和自己建边
            while(k2--){//饮料是 (f+n+n+1)-(f+n+n+d)
                int t;
                cin>>t;
                g[f+n+i][f+n+n+t]=1;//牛和饮料建边
            }
        }
        for(int i=f+n+n+1;i<=f+n+n+d;i++){//和汇点建边
            g[i][all]=1;
        }
        cout<<ek(0,all)<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/95023869
今日推荐