POJ 3281:Dining

题目链接:http://poj.org/problem?id=3281



题目翻译:农夫约翰为它的牛准备了F种食物,D种饮料,每头牛有自己

喜欢的食物和饮料,而每种食物只能分配给一头牛,最多能有多少头牛

可以同时得到喜欢的食物和饮料?


解题思路:

构建网络流:

边的方向   S(超级源点)->食物(F种食物)->牛->牛->饮料(D种饮料)

其中牛到牛是拆点,而且这个题目必须拆点,这个题目是最大流题目,

它属于顶点上有容量限制的最大流,对于容量有限制的顶点,我们必须

要拆点,这个边的容量就是顶点的容量。和POJ 3436:ACM Computer Factory

是一样的,都要拆点,原来我还不太明白为什么非要拆点,但是看了挑战程序

设计之后明白了,拆点之后就达到了限流的作用。否则对于一头牛它有可能

会得到多于一组的食物和饮料的搭配。



AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <vector>

using namespace std;

const int maxn = 500;
const int INF = 0x3f3f3f3f;
int N,F,D;   ///F种食物,D种饮料,N头牛
int S,T;     ///超级源点,超级汇点。
int cap[maxn][maxn];  ///容量
int level[maxn],vis[maxn];
int maxFlow;
///广搜分层
bool bfs()
{
    queue<int>qu;
    memset(level,-1,sizeof(level));
    level[S] = 0;
    qu.push(S);
    while(!qu.empty())
    {
        int u = qu.front();
        qu.pop();
        for(int i = 0; i <= T; i++)
        {
            if(level[i]==-1 && cap[u][i]>0)
            {
                level[i] = level[u] + 1;
                if(i == T)
                    return true;
                qu.push(i);
            }
        }
    }
    return false;
}
///求增广路径
void dfs()
{
    int u,v,cur,i;
    deque<int>qu;  ///双端队列,当作栈用
    memset(vis,0,sizeof(vis));
    vis[S] = 1;
    qu.push_back(S);
    while(!qu.empty())
    {
        cur = qu.back();
        if(cur == T)
        {
            for(i = 1; i < (int)qu.size(); i++)
            {
                u = qu[i-1];
                v = qu[i];
                cap[u][v] -= 1;
                cap[v][u] += 1;
                vis[v] = 0;   ///直接取消标记
            }
            maxFlow += 1;  ///图中有权值为1或0的边,座椅一条路中的最大最小流量都是1.
            qu.clear();
            qu.push_back(S);  
            ///这种情况其实每次都是从S开始回溯的,因为找到一条路后,这整条路就断了。
        }
        else
        {
            for(i = 1; i <= T; i++)
            {
                if(cap[cur][i]>0 && level[i] == level[cur]+1 && vis[i]==0)
                {
                    vis[i] = 1;
                    qu.push_back(i);
                    break;
                }
            }
            if(i > T)
                qu.pop_back();
        }
    }
}
void dinic()
{
    maxFlow = 0;
    while(bfs())
    {
        dfs();
    }
    printf("%d\n",maxFlow);
}
int main()
{
    while(~scanf("%d%d%d",&N,&F,&D)) ///N牛的数量,F食物种类,D饮料种类
    {
        S = 0;                       ///超级源点
        T = 2*N+F+D+1;               ///超级汇点
        ///0是超级源点,1~2*N是牛拆点,2*N+1~2*N+F是食物,2*N+F+1~2*N+F+D是饮料
        memset(cap,0,sizeof(cap));
        ///超级源点向每种食物引一条边
        for(int i = 2*N+1; i <= 2*N+F; i++)
        {
            cap[S][i] = 1;
        }
        ///每种饮料向超级汇点引一条边
        for(int i = 2*N+F+1; i <= 2*N+F+D; i++)
        {
            cap[i][T] = 1;
        }
        ///每个牛拆成两个点,来达到限流的目的
        for(int i = 1; i <= N; i++)
        {
            cap[i][i+N] = 1;
        }
        int food,drink;
        for(int i = 1; i <= N; i++)
        {
            scanf("%d%d",&food,&drink);
            int u;
            for(int j = 1; j <= food; j++)
            {
                scanf("%d",&u);  ///食物向原来牛的点引边,食物编号2*N+u
                u = 2*N+u;
                cap[u][i] = 1;
            }
            for(int j = 1; j <= drink; j++)
            {
                scanf("%d",&u);  ///从牛拆出的点向饮料引边,牛的编号i+N,音量编号2*N+F+u
                u = 2*N+F+u;
                cap[i+N][u] = 1;
            }
        }
        dinic();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值