POJ3281 Dining 最大流入门 Dinic算法

题意:
有N头牛,F种食物可以制作,D种饮料可以制作 然后每行代表一头牛的喜好,开头两个数fi,di表示这头牛喜欢fi种食物,di种饮料,接下来fi个数表示喜欢的食物编号,di个数表示喜欢的饮料的编号
现在主人使用最优决策制作出F种食物和D种饮料,问怎么喂才能使尽可能多的牛喂饱(喂饱=一份食物一份饮料,且一头牛最多消耗一份食物和一份饮料) , 输出最多喂饱的牛数
思路:
本题关键是建图,图建完以后就是裸 dinic
Dinnic算法模板

建图方法:
设1+F+D+2N+1个结点,1表示源点,1+F+D+2N+1表示汇点 *
1+1 ~ 1+F表示食物结点,1+F+1 ~ 1+F+N表示牛的编号,1+F+N+1 ~ 1+F+2N也表示牛的编号,为什么这样后面说明 * 1+F+2N+1 ~ 1+F+2*N+D表示饮料编号 * * 然后被喜欢的食物指向喜欢它的牛的所有边,牛i指向牛i的所有边,牛指向它喜欢的饮料的所有边,源点到所有食物,饮料到所有汇点的边,权值全设为1 * *
建图解释:
首先,这幅图被分为几个模块,从左到右依次为:食物制作模块(src-F边集),喂食物模块(F-N1边集),牛吃食物模块(N1-N2边集),喂饮料模块(N2-D边集)和牛喝饮料模块(D-tar边集) 然后解释为什么设权值为1,
第一:人做的食物和水都是一份;第二:牛最多吃一份食物(水),就算有多份食物指向牛,由于下一条边(牛指出的边,即牛吃食物(喝饮料)模块)为1,意味着最后只能流出1的流量,即吃掉其中一份食物,水的指出同理 ,这样就能想到为什么要设两次牛了,就是要通过牛-牛边控制流量为1,即通过食物选择模块后,保证一头牛只吃了一份食物 , 然后吃完食物流量为1了,就只能选一条路喝饮料,最后饮料流出的也是唯一的一条边,表示这种饮料最终只能被一头牛选择

//#include<bits/stdc++.h>
#include<cstdio>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=1e3+10;

int F,D,N;
int g[maxn][maxn];
int lv[maxn];

//bfs分层图过程
bool bfs(int src,int tar)
{
    memset(lv,0,sizeof lv);
    queue<int>que;
    que.push(src);
    lv[src]=1;//源点的深度为1

    while(!que.empty()){
        int cur=que.front();
        que.pop();
        for(int i=src;i<=tar;i++)
        //若该残量不为0,并未分配深度
            if(!lv[i]&&g[cur][i]){
                lv[i]=lv[cur]+1;
                if(i==tar)  return 1;
                que.push(i);
            }
        }
    return 0;
}

//寻找增广路
//cur是当前节点,src是源点,tar是汇点,totflow当前流量
int dfs(int cur,int src,int tar,int totflow){
    int ret=0;
    if(cur==tar||!totflow)//当已经到达汇点,直接返回
        return totflow;
    for(int i=src;i<=tar;i++){
        if(totflow==0)  break;//若没有增广路
    //满足分层图和残量不为0两个条件
        if(g[cur][i]&&lv[cur]+1==lv[i]){
            int f=min(totflow,g[cur][i]);
            int flowdown=dfs(i,src,tar,f);//向下增广
            ret+=flowdown;
            totflow-=flowdown;
            g[cur][i]-=flowdown;//正向边减
            g[i][cur]+=flowdown;//反向边加
        }
    }
    return ret;
}

int dinic(int src,int tar,int num)
{
    int ret=0;//记录最大流量
    while(bfs(src,tar)){
        int tmp=dfs(src,src,tar,num);
        if(!tmp)    break;
        ret+=tmp;
    }
    return ret;
}

int main()
{
    while(scanf("%d%d%d",&N,&F,&D)!=EOF){
        int src=1;//源点
        int tar=1+N*2+F+D+1;//汇点

        for(int i=1;i<=F;i++)//src-F边集
            g[src][src+i]=1;

        for(int i=1+N*2+F+1;i<tar;i++)//D-tar边集
            g[i][tar]=1;

        for(int i=1;i<=N;i++){
            g[src+F+i][src+F+i+N]=1;//N1-N2边集
            int fi,di;
            scanf("%d%d",&fi,&di);

            int tmp;
            while(fi--){//F-N1边集
                scanf("%d",&tmp);
                g[src+tmp][src+F+i]=1;
            }
            while(di--){
                scanf("%d",&tmp);
                g[src+F+i+N][src+F+2*N+tmp]=1;
            }
        }
        printf("%d\n",dinic(src,tar,D+F));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40507857/article/details/82915134