网络流入门2——最大流(关于为什么要拆点的问题)

本文通过POJ 3281 Dining问题介绍最大流在网络流问题中的应用,解释如何通过拆点解决食物和饮料分配的限制,确保每只奶牛只能吃一种食物和一种饮料。通过建立流网络模型并利用容量限制,找到能服务的最大人数。

POJ 3281 Dining (USACO 2007 Open Gold)

题目意思比较简单,就是说现在有 N 只奶牛,F 种食物和 D 种饮料,每只奶牛喜欢其中的一些食物和饮料。现在每种食物和饮料只能分给一只奶牛,每只奶牛也只能吃一种食物和一种饮料,问最多能使多少奶牛既吃到食物又喝到饮料。
这个题和二分图匹配有相似之处,但又不完全相同,我们可以沿着二分图匹配的建模方式继续思考。
由于有 N 只奶牛、F 种食物和 D 种饮料,因此我们可以将这些东西抽象成图中的点。为了方便,我们将食物放在左边,奶牛放在中间,饮料放在右边。沿用前面的建模方式,由于食物和饮料的使用限制,我们从源点向每种食物连一条边,从每种饮料向汇点连一条边,容量都为 1。而每只奶牛都有喜欢的食物和饮料,因此将每只奶牛喜欢的食物连到这只奶牛,从这只奶牛连到每种它喜欢的饮料。
但这样是否就对了呢?实际还是有问题的,因为经过每只奶牛的食物可能超过一种,这就意味着每只奶牛可能会吃超过一组的食物和饮料,而这在题目中是不允许的。
怎 么 解 决 这 个 问 题 呢 ? 我 们 又 回 到 了 流 的 基 本 性 质 : 容 量 限 制f(u,v)<=c(u,v) 。因此我们将每只奶牛拆成两个点,同一只奶牛的两个点之间连边,容量为 1。这样我们就能保证通过每只奶牛的流量为 1 了。

每个流对应每种方案,最大流即为最佳方案。


可见最大流模型的一般建模思路是运用流的容量限制,使得题目中的约束得以满足,有时还需使用一些特殊的方法(如上题中的拆点)来满足题目的特别约束。

代码:

#include<iostream>  
#include<stdio.h>  
#include<string.h>  
using namespace std;  
const int maxE = 200000;  
const int maxN = 5005;  
const int maxQ = 10000;  
const int oo=0x3f3f3f3f;  
struct Edge{  
    int v, c, n;  
};  
Edge edge[maxE];  
int adj[maxN], cntE;  
int Q[maxE], head, tail, inq[maxN];  
int d[maxN], num[maxN], cur[maxN], pre[maxN];  
int sink,source,nv;  
void add (int u, int v, int c) {//添加边  
    //正向边  
    edge[cntE].v = v;  
    edge[cntE].c = c;//正向弧的容量为c  
    edge[cntE].n = adj[u];  
    adj[u] = cntE++;  
  
    //反向边  
    edge[cntE].v = u;  
    edge[cntE].c = 0;//反向弧的容量为0  
    edge[cntE].n = adj[v];  
    adj[v] = cntE++;  
}  
  
void rev_bfs () {//反向BFS标号  
    memset(num,0,sizeof(num));  
    memset(d,-1,sizeof(d));  
    //clear (d, -1);//没标过号则为-1  
    d[sink] = 0;//汇点默认为标过号  
    num[0] = 1;  
    head = tail = 0;  
    Q[tail++] = sink;  
  
    while (head != tail) {  
        int u = Q[head++];  
        for (int i = adj[u]; ~i; i = edge[i].n) {  
            int v = edge[i].v;  
            if (~d[v]) continue;//已经标过号  
            d[v] = d[u] + 1;//标号  
            Q[tail++] = v;  
            num[d[v]]++;  
        }  
    }  
}  
  
int ISAP() {  
    memcpy(cur,adj,sizeof(cur));  
   // copy (cur, adj);//复制,当前弧优化  
    rev_bfs ();//只用标号一次就够了,重标号在ISAP主函数中进行就行了  
    int flow = 0, u = pre[source] = source, i;  
  
    while (d[sink] < nv) {//最长也
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值