【您有新的未分配天赋点】网络流:从懵逼到完全懵逼

今天呢@assassain julao讲了一个在OI中极其重要,极其有趣,把无数人坑退役的知识点:网络流。

网络流呢顾名思义,就是在一个图中边有流量的限制,并根据这些流量限制做一些跟这个有关的事(ti)情(mu)。什么,范围?按zzh神犇的话来说,就是考试中那些看上去像是dp却又推不出式子的问题的通用解法。

按照问题的倾向,我们将问题分为三类:最大流、最小割、最小费用流。

大家看好我要开始口胡了

首先我们研究最大流,介绍最大流大部分解法原理,增广路定理:只要存在增广路,流就可以继续增大。证明显而易见,这里省去其实是我不会证。运用这一定理的算法有多种,这里只介绍最常见的Dinic算法。Dinic的原理可以概括为“层次图”和“阻塞流”,即不断使用bfs构造出层次图,在层次图中每一层多路增广,卡掉小边,最后从源点到不了汇点的时候流的大小即为最大流。图中最多有n层,会扩展n次,每次扩展中有n个节点搜索,进退流m次,因此单次增广时间复杂度为$O(nm)$,总的时间复杂度上限为$O(n^2m)$,实际上远没有这么差,如果是二分图匹配这种情况,甚至可以证(chui)明(bi)出时间复杂度为$O(\sqrt{n}m)$,浮动太大,因此接下来我们将不再讨论时间复杂度问题明明是你太蒻讨论不了啊喂,只认为时间复杂度有两种可能:$O(能过)$、$O(不能过)$。直接贴板子。

 1 int S,T;
 2 int f[maxn][maxn],num[maxn][maxn],cnt,dist[maxm];
 3 int flag[maxn][maxn];
 4 bool bfs()
 5 {
 6     memset(dist,-1,sizeof(dist));
 7     dist[S]=1;
 8     queue<int>q;q.push(S);
 9     while(!q.empty())
10     {
11         int t=q.front();q.pop();
12         for(int i=head[t];i!=-1;i=edge[i].next)
13         {
14             int v=edge[i].to;
15             if(edge[i].flow>0&&dist[v]<0)
16             {
17                 dist[v]=dist[t]+1;
18                 q.push(v);
19                 if(v==T)return 1;
20             }
21         }
22     }
23     return 0;
24 }
25 int dfs(int pos,int flow)
26 {
27     if(pos==T)return flow;
28     for(int i=head[pos];i!=-1;i=edge[i].next)
29     {
30         int v=edge[i].to;
31         if(dist[pos]+1==dist[v]&&edge[i].flow>0)
32         {
33             int t=dfs(v,min(flow,edge[i].flow));
34             if(t>0)
35             {
36                 edge[i].flow-=t;
37                 edge[i^1].flow+=t;
38                 return t;
39             }
40         }
41     }
42     return 0;
43 }
44 int Dinic()
45 {
46     int ans=0,cnt;
47     while(bfs())
48            while(cnt=dfs(S,inf))ans+=cnt;
49     return ans;
50 }

板子

接下来我们继续口胡来看最小割,对于最小割有最小割定理:最小割=最大流。证明如下:

开始Ctrl+C @assassainPPT

证明:对于一个割来说,所有从s到t的流量必定经过删除的边,那么Max_flow一定 ≤ 割的值,同理可以推出Max_flow ≤ 任意割的值。
下面来看一个已经跑完最大流的残余网络,此时图中已没有从s到t的路径。将s和s能到达的所有点划分为S集,剩余点为T集。中间的所有边为一个割且均满载(剩余容量为0),那么当前流也就是最大流等于割的值,又因割的值大于等于最大流,所以此时的割即为最小割,且与最大流相等。

Ctrl+C完啦

最后我们来看玄学之王费用流。通过最大流定理我们可以知道找增广路就可以得到最大流,那么我们只要在找增广路时贪心找到单位流量费用最小增广路即可。这个证明比较简单,留给读者思考。你又不会了是不是啊。根据思(chang)考(shi),我们可以发现SPFA的时间复杂度比较小,可以保留在Dinic接近$O(不能过)$时过掉的希望。然而SPFA自己的时间复杂度也是$O(玄学)$的,因此,费用流的时间复杂度更加难以预测,大多数时间只能看情况考虑赌脸。

(证明自己是个欧洲人的机会来了)

板子嘛……还没打……留坑待填(逃

今天大概就是这样,如果还有什么新的体会我会继续更新然而你不是哪篇最后都没更新完成……

时间: 2024-08-07 04:31:22

【您有新的未分配天赋点】网络流:从懵逼到完全懵逼的相关文章

【您有新的未分配天赋点】计算几何:从被纸笔支配的恐怖到达被代码支配的恐怖

开坑时间:2017/8/5 21:25 今天呢$lgl$神犇终于打开了坑害了无数英雄好汉的新陷阱的盖子新世界的大门:计算几何.尽管再不愿意但该来的还是要来.于是我就这么踏上了这条贼船--(缓更,勿催) 本文将对现阶段所需的四项知识做出一些主观的感性分析,希望大家能够就此感性理解一下. 一.凸包 凸包,顾名思义就是把给定点围在里面的最小凸多边形.这个东西一般采用扫描方法进行处理,时间瓶颈为排序的$O(nlogn)$. 链接:http://www.cnblogs.com/Loser-of-Life/

[您有新的未分配科技点]博弈论进阶:似乎不那么恐惧了…… (SJ定理,简单的基础模型)

这次,我们来继续学习博弈论的知识.今天我们会学习更多的基础模型,以及SJ定理的应用. 首先,我们来看博弈论在DAG上的应用.首先来看一个小例子:在一个有向无环图中,有一个棋子从某一个点开始一直向它的出点移动,双方轮流操作,无法操作者输,问是否先手必胜. 考虑一下我们之前的Nim游戏,如果我们把后继状态看成后继点的话,不难发现Nim游戏的互相转移也是一个DAG.因此,DAG上出度为0的点的sg值为0,再用上一篇博客提到的mex操作来求每个点的值就可以了(注意,这并不是一个"大"子图,不能

[您有新的未分配科技点]可,可,可持久化!?------0-1Trie和可持久化Trie普及版讲解

这一次,我们来了解普通Trie树的变种:0-1Trie以及在其基础上产生的可持久化Trie(其实,普通的Trie也可以可持久化,只是不太常见) 先简单介绍一下0-1Trie:一个0-1Trie节点只有两个子节点,分别代表0和1:从根节点开始,第一层代表限制的最高位,依次往下直到最底层,代表二进制第0位. 0-1Trie上的一条链所表示的数字,就是Trie树中的一个数字.0-1Trie除了节点和插入方式与普通的Trie树略有不同之外,其他操作都是和Trie树完全一样的.在维护这个节点插入过的siz

[您有新的未分配科技点]可,可,可持久化!?------可持久化线段树普及版讲解

最近跑来打数据结构,于是我决定搞一发可持久化,然后发现--一发不可收啊-- 对于可持久化数据结构,其最大的特征是"历史版本查询",即可以回到某一次修改之前的状态,并继续操作:而这种"历史版本查询"会衍生出其他一些强大的操作. 今天,我们主要讲解可持久化线段树.其实,它的另外一个名字"主席树"似乎更加为人所知(主席%%%). 主席树与普通的线段树相比,多出来的操作是在修改时复制修改的一条链,这个操作的过程大概长下面这样. 至于为什么要这样做-- 对

[您有新的未分配科技点]数位dp:从懵X到板子

数位dp主要用来处理一系列需要数数的问题,一般套路为"求[l,r]区间内满足要求的数/数位的个数" 要求五花八门--比如"不出现某个数字序列","某种数的出现次数"等等-- 面对这种数数题,暴力的想法是枚举每个数,判断是否满足条件 比如这样: #include<cstdio> using namespace std; typedef long long LL; LL l,r,cnt; int main() { scanf("

[您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)

今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我们还是用rand()来实现平衡 而无旋treap与treap不同的地方,也是其核心,就是它不旋转用两个新的核心函数:merge函数(合并两棵子树)和split函数(分裂出某棵树的前k个节点,并且作为一棵树返回) 首先看merge函数,它是一个递归实现的过程,先看代码: 1 Treap *merge(

[您有新的未分配科技点]数位DP:从板子到基础

只会统计数位个数或者某种"符合简单规律"的数并不够--我们需要更多的套路和应用 数位dp中常用的思想是"分类讨论"思想.下面我们就看一道典型的分类讨论例题 1026: [SCOI2009]windy数 Time Limit: 1 Sec  Memory Limit: 162 MB Description windy定义了一种windy数.不含前导零且相邻两个数字之差至少为2的正整数被称为windy数. windy想知道,在A和B之间,包括A和B,总共有多少个wind

[您有新的未分配科技点][BZOJ3545&amp;BZOJ3551]克鲁斯卡尔重构树

这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰有他的高度h_i.有些山峰之间有双向道路相连,共M条路径,每条路径有一个困难值,这个值越大表示越难走. 现在有Q组询问,每组询问询问从点v开始只经过困难值小于等于x的路径所能到达的山峰中第k高的山峰,如果无解输出-1.N<=1e5,M,Q<=5*1e5 上面这个题没有要求在线,因此我们可以离线构造

[您有新的未分配科技点]计算几何入门(1):点,向量以及向量的简单应用

在打了一阵数据结构之后,老师表示"今天晚上让学长给你们讲一下计算几何"--然后就死了.jpg 昨天晚上一直在推数学的式子以及回顾讲课的笔记--计算几何特点就是多而杂,即使是入门部分也是如此-- 首先,我们从二维的几何问题开始处理. 我们知道,高中解析几何计算几何的基础是向量(Vector)和点(Point),所以我们先来表示这两个概念: 在计算几何中,点和向量一般用结构体来存储,像这样: 1 struct Point 2 { 3 double x,y,rad; 4 Point(doub