网络流

网络流(最大流)

对于网络流求最大流的问题,可以想象为从起点倒水(倒无限多的水),然后问终点的水量.
所以一条路上为终点添加的流量为这条路上流量最小的路径.
(我把边能通过的流量称为流量,水的量也称为流量)

首先可以有一个贪心的想法,
每次可以让尽量大的流量通过边.

这里写图片描述

好像是已经是最大流了(已经没有水能到终点了),这种做法其实还有问题.
每次流入尽量多的水没有问题,每条边让尽量大的流量通过也没有问题,但是这样的贪心是没有远见的,它可能会把路堵住,导致答案变小(程序里的水不会自己流动).

这里写图片描述

(虽然我们可以很容易看出它的错误,但是数据一大就没有办法了,所以这样的贪心是不行的)

在普通的贪心失败后,往往可以采取回撤类型的贪心:
首先,按照前面的贪心一直加大流量.
然后,流量无法再加大了.于是,加入新的水流,让原来的水倒流回去,位新的水流腾出位置.
最后,无论如何也腾不出位置了,最大流就求出来了.

对于上个图,从3流入30的流量,但是这部分的流量没有地方去,所以让4倒流10的流量,经过5和2流向终点,并从3中得到10的流量.于是,我们增加了10的流量

这样好像很复杂(还要从源头修改),
但是这部分的操作也可以直接简化为3-5-2流动的过程

所以,在储存每一条边的时候,再额外存储一条反向边(它的可以通过的流量为0).
当水流通过时,正向边的流量减小,反向边的流向增大(表示正向可以通过的流量减小,而可以倒流的流量增大)
最后,当无法增大流量时,就求出了最大流.

const int M=1005;
const int INF=1<<28;

struct node {
    int to,cap,bk;
    /*e[to][bk]指向自己*/ 
};
struct graph {
    vector <node> e[M];
    bool mark[M];
    void clear() {
        for (int i=0;i<M;++i) e[i].clear();//清空; 
    }
    void addedge(int st,int ed,int cap) {
        e[st].push_back((node){ed,cap,(int)e[ed].size()}); 
        e[ed].push_back((node){st,0,(int)e[st].size()-1});//储存反边 (流量为0)
    }
    int dfs(int st,int ed,int f) {
        if (st==ed) return f;
        mark[st]=true;
        for (int i=0;i<(int)e[st].size();++i) {
            node &now=e[st][i];
            if (mark[now.to]||!now.cap) continue;
            int d=dfs(now.to,ed,min(f,now.cap));
            if (d) {
                now.cap-=d;
                e[now.to][now.bk].cap+=d;//为反边增加大流量 
                return d;
            }
        }
        return 0;
    }

    int flow(int st,int ed) {
        int flow=0;
        while (true) {
            memset(mark,0,sizeof(mark));
            int f=dfs(st,ed,INF);
            if (!f) return flow;
            flow+=f;
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值