网络流

一、网络流

乱七八糟的定义
1、网络流问题中我们主要研究最大流以及费用流问题,就是给定一张图G(V,E),每条边限制其流量,问从一个点到另一个点能通过的最大的流量之和是多少。
一条路径上的流量大小是这条路径上流量最小边的流量。
2、增广路:一条能从起点到终点的,流量不为0的路径
3、残量网络:对于图中的一些路径进行增广后剩下的图。
3、割:流网络的割(S,T)将原图划分为 S 和 T 两部分,使得 s ∈ S,t ∈ T。一个网络的最小割就是网络中所有割中最小容量的割。
4、最小割:最小割可以理解为,你将一个图分为两部分,然后将图割开,使这个图的两个部分互不联通。(思路有点类似二分图,可以理解为二分图多重匹配,因为流量不止是1.也就是流量如果是1,那么就等于这个每个点只能往一个点流,那么就是匈牙利算法可以解决的二分图问题了。)
还有一种常见的使用方式就是用它来处理图的冲突的选择和并行情况。
最小割=最大流
方法:对于网络流问题,我们处理的方式就是通过不断地寻找增广路,将每一条路径上的流量跑满,最终不存在增广路时的流就是最大流。
5、反向边:在网络流算法当中我们会用到反向边这个概念,反向边就是在算法中给我们提供一个反悔的机会,因为每次我们在处理增广路的时候不一定是通过最佳情况进行的处理。

发现一篇精彩的博客https://www.cnblogs.com/ZJUT-jiangnan/p/3632525.html

DINIC算法

思路:通过bfs来判断起点和终点是否还联通并且分层,如果不连通就表示算法已经结束。然后分层结束后用dfs来处理这张图,来寻找增广路进行增广。
代码

void bfs(){
memset(d,-1,sizeof(d));
queue<int> q;
q.push(st);
d[st]=1;
while(!q.empty()){
    int nown=q.front();
    q.pop();
    for(int i=head[nown];i!=-1;i=a[i].nextx){
        int ee=a[i].end;
        if(a[i].edge>0&&d[ee]==-1){
            d[ee]=d[nown]+1;
            q.push(ee);
        }
    }
}
return d[ed]!=-1;
} 
int dfs(int start,int end,int nowdat){
int dat=0;
if(start==end)return nowdat;
for(int i=head[start];i!=-1;i=a[i].nextx){
    int ee=a[i].end;
    if(a[i].edge>0&&d[ee]==d[start]+1){
        int nown=min(nowdat-dat,a[i].edge);
        nown=dfs(ee,end,nown);
        dat+=nown;
        a[i].edge-=nown;
        a[i^1].egde+=nown;
    }
}
if(!dat)d[start]=-2;
return dat;
}
void dinic(){
ans=0;
while(bfs())
    ans+=dfs(st,ed,0x3f3f3f3f);
return;
}
EK算法

思路也是寻找增广路然后进行增广。
相比起dinic算法,EK算法使用bfs来寻找增广路,然后记录路径,回溯来增广这条边。
常数比dinic相对较高,但却是费用流算法MCMF的基础。

例题

cdoj 1432
每个奇数点向源点连边,每个偶数点向汇点连边,奇数点和偶数点之间连边,每次选择一组点就是将他们之间的边割开,就变成最小割问题了。(巧妙!!!)
poj 1273
最大流模板题
还没写过,不过挂一个链接http://blog.csdn.net/huzhengnan/article/details/7766446
UVALIVE 7264
巧妙地建图(话说我差一点点就对了):
每个点拆成两个点,之间连一条ci表示直接氪掉这个技能。
父节点向它连一条流量为bi的边,表示断掉这条边的代价。
源点向它连一条ai的边,就是你要正常学习这个技能要花费的代价。
hdu 3987
相当于是双关键词的权值
通用处理技巧:将第一关键字乘上(第二关键字的最大值+1),再加上第二关键字即可

费用流

定义:(和最大流基本类似,不过引入了费用这个概念。)
每条边产生的费用就是这条边的边权*这条边的流量。
大多数情况下是求最小费用最大流,就是先要满足满流,再满足费用小。
有的时候也是最大流最小费用,就是引入一个利益概念,你要利益最大。
算法
把EK算法的bfs换成spfa,遍历改成最短路,进行增广即可。
一直寻找当前能找到的最短路,进行增广,最后直到流量最大。
(反向边就把边权取反)
例题
hdu 6118
生产一个商品需要花费 a[i] 元,且上限为 b[i] ,所以我们从 s(超级源点) 向这些点之间连一条容量为 b[i] ,费用为 -a[i] 的边。
出售一个商品可以赚到 c[i] 元,最多出售 d[i] 个,于是我们从这些点向 t(超级汇点) 连一条容量为 d[i] ,费用为 c[i] 的边。
对于所有的公路,从 u 到 v 连接一条双向边,容量为 INF ,费用为 -k ,然后跑一边最小费用最大流。
图中存在自环,当两点路径长度小于0时应终止计算
hdu4494
每个点拆成两个点,一个自己用,一个往外提供。
源点向自己用的点连流量为需要工人数量,费用为1的边。向往外提供的点连流量为需要工人数量,费用为0的边。
每个自己点向汇点连流量为需要工人数量,费用为0的边,这个用来帮助跑满流。
然后每个发散点往能去的地方连inf,0的边。
跑模板。

最大权闭合图

在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。最大权闭合图即在所有闭合图中,集合中点的权值(可能为负)之和最大的V,我们称V为最大权闭合图。

处理方法
转化为最小割进行操作
源点向所有权值为正的点连一条流量为权值的边,然后每个负权点向汇点连一条流量为权值绝对值的边,图中原先含有的边流量变为正无穷。
最后答案就是所有正权点的权值减去新图的最小割。
证明
证明这个问题之前先证明两个定理。
一个是简单割,简单割就是这个割的所有边都和s以及t有关联。
在这个图中的最小割=简单割。
因为除了和st连的边剩下的边都是正无穷的了,最小割不会割正无穷的边。
第二就是,每个简单割都会和一个闭合子图有联系。
这个有一种比较通俗的解释方式,就是在产生的新图当中,每个源点连出边割开,表示在原图中的这个点不选,而每个汇点连向边割开,就表示这个点被选上了。所以每一个简单割就是一个闭合子图。
实际上每条路径能产生的价值就是你要么选择这条路径,那么就可以获得这条路径上的正权值以及负权值。要么就是不要这条路径,产生的结果是0.
而我们是用所有正权值之和减去这个答案,所以你每个路径如果选择就是减去连向汇点的边的权值,如果不选择就是减去点连出的权值边。
例题
hdu 5855
工厂是负值,售卖是正值,建好工厂才能卖是先后条件。
然后枚举时间或者二分时间就行了。
hihocoder1398
比上一题还裸,每个学生是产生负值的原因,每个活动是产生正值的原因。

二分图带权匹配

其实就是匹时每条边有权值,使权值最大。
算法
KM算法:
(1) 初始化可行标杆
(2) 用匈牙利算法寻找完备匹配
(3) 若未找到完备匹配则修改可行标杆
(4) 重复(2)(3)直到找到相等子图的完备匹配
可行标杆就是每个妹子想要找到几分的汉子,修改可行标杆就是妹子们降低自己的期望值,去找其他的(次一点的)汉子
例题
UVVALIVE6129
先处理出第一步中所有人的消耗的总时间最短。
然后用这一步中我们已经使用计算过的时间来处理第二批沙发的情况。
或者是用费用流做。
因为你可以理解为你建立一张图,你需要所有的点完成的费用最小。
(其实就像二分图题可以使用dinic做一样,几乎所有的KM题也可以使用费用流来处理)
poj2195:模板题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值