zoukankan      html  css  js  c++  java
  • 【学习笔记】网络流的简单入门(更新中)

    声明:本博客所有随笔都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

    前言

    终于学习到了网络流了~

    其实网络流算法真正难的在于建模,如何将问题转化为网络流解决。很多看似和网络流不沾边的题目其实都可用次来解。一入网络流深似海,路漫漫其修远兮~

    (本篇文章全文主要借鉴这篇blog,写得超级棒哒)


    [٩(ˊᗜˋ)و✧* ]

    一.概念及性质

    网络流基本概念

    ·网络流:一种类比水流的解决问题方法

    ·网络:可以看做有源点和汇点的有向图

    ·弧:有向图的边/水管

    ·源点:网络中没有入边的点/起点(即可以无限出水的水源),表示为 (S)

    ·汇点:网络中没有出边的点/终点(即可以无限收集水的点),表示为 (T)

    ·容量:一条边可以承受的最大水量,每条边/弧都会有一个容量,表示为函数 (c(u, v))

    ·流量:一条边当前承受的水量,每条边/弧都会有一个流量,表示为函数 (f(u, v));流量可为 (0),可为负

    残留容量:一条边还可以新增加的水量,即为 容量 - 流量

    ·容量网络:每条边都给出了容量的网络

    ·流量网络:每条边都给出了流量的网络

    ·残量网络:每条边都给出了残留容量的网络,即为 容量网络 - 流量网络

    网络流的三大性质

    ·容量限制(f(u,v)≤c(u,v)) 一条边的流不能超过它的容量

    ·流守恒:除非 (u=s)(u=t) ,否则 (sum_{(w∈V)}f(u,w)=0) 一结点的净流是零,除了“制造”流的源点和“消耗”流的汇点。

    (意思就是,除了源点和汇点外,一条边流进来了多少水就会流出去多少水,保证守恒)

    ·斜对称(f(u, v) = -f(v, u))

    (这个其实也不太好理解...暂时理解为矢量?之后会有很多运用)

    [٩(ˊᗜˋ)و✧* ]


    [٩(ˊᗜˋ)و✧* ]

    二.最大流

    概念

    ·网络的流量:流量网络的汇点收集到的流量值

    ·最大流:网络的流量的最大值

    FF方法

    概念

    增广路:在残量网络中的一条从 (S)(T) 的合法路径,即所有弧的残量为正
    **
    增广路定理:网络达到最大流当且仅当残量网络中没有增广路

    (FF(Ford−Fulkerson)) 方法又称增广路方法:不断地在残量网络中找到一条从 (S)(T) 的增广路,向 (T) 发送流量,修改此条增广路上弧的残量,直到找不到增广路为止

    EK算法

    (EK):基于 (FF) 方法的一种算法

    大致流程:

    (1.)(bfs) 找一条增广路,逆向记录下这条路上的弧

    (2.)(ans) 加上这条增广路上的流量(木桶定理

    (3.)更改此条路径上弧的残量

    (细节请看代码)

    详细介绍

    但是有个问题,就是如何保证我每次随机选择的增广路都是最优的?或者说,是可以达到最大流的,而不会影响后面继续找增广路?

    比如下面这张图,第一次找增广路时我们找到了蓝色的这条((1 -> 2 -> 3 -> 4)

    那么下一次我们就无法找出增广路了;可是这就是最大流了吗?

    显然不是,如果选择 (1->2->4)(1->3->4) 最后答案就为 (2),比刚才要大

    所以我们得给选过的增广路一个反悔的机会

    在最初建图时,我们把每条边都建一条流量为(0)的反边

    选择一条增广路时,除了把这条增广路上弧的残量修改之外,我们还把每条弧的反边加上此次的流量

    例如刚才那张图,我们选择了第一条增广路后,它应该变成这样

    接下来我们模拟一下第二次找增广路

    首先从 (1) 找到了 (3),这条弧上残量为正,是合法的

    接下来从 (3) 开始找,发现可以流向 (2),从 (2) 又可以流向 (4),即找到了一条增广路,(ans) 变为 (2)

    为什么可以这样做?

    其实很简单。当我们找到 (3) 的时候,和原来那条增广路碰面了,也就是说,从 (3) 这里往后走一定会有一条合法的流到达汇点(就是原来那条增广路),于是我们把 (3) 以及之后的那条合法路径全部割让给 (1->3) 这里, 让 (2) 不往 (3) 这里走,而是转头去找 (4)

    感觉说的有点乱...

    引用一段这位大佬写的话

    将之前流出去的流量的一部分(或者全部)反悔掉了个头,跟随着新的路径流向了其它地方,而新的路径上在到达这条边之前所积蓄的流量 以及 之前掉头掉剩下的流量 则顺着之前的路径流了下去。

    时间复杂度

    (O(nm^2))

    Code

    #include<cstdio>
    #include<iostream>
    #include<fstream>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #define F(i, x, y) for(register int i = x; i <= y; ++ i)
    using namespace std;
    int read();
    const int N = 1e4 + 5;
    const int M = 1e5 + 5;
    const int inf = 0x3f3f3f3f;
    int n, m, s, t, ans;
    int head[N], cnt = 1, ver[M << 1], edge[M << 1], next[M << 1];
    int val[N], vis[N], a[N];
    queue<int> q;
    void add(int x, int y, int z){ver[++ cnt] = y, edge[cnt] = z, next[cnt] = head[x], head[x] = cnt;}
    bool bfs()
    {
    	while(q.size()) q.pop();
    	memset(vis, 0, sizeof(vis));
    	q.push(s), vis[s] = 1, val[s] = inf;
    	while(q.size())
    	{
    		int x = q.front(); q.pop();
    		for(int i= head[x]; i; i = next[i])
    			if(edge[i] && ! vis[ver[i]])
    			{
    				val[ver[i]] = min(edge[i], val[x]);
    				q.push(ver[i]), vis[ver[i]] = 1, a[ver[i]] = i;
    				if(ver[i] == t) return true;
    			}
    	}
    	return false;
    }
    void EK()
    {
    	while(bfs())
    	{
    		int x = t; ans += val[t];
    		while(x != s)
    			edge[a[x]] -= val[t], edge[a[x] ^ 1] += val[t], x = ver[a[x] ^ 1];
    	}
    }
    int main()
    {
    	n = read(), m = read(), s = read(), t = read();
    	for(int i = 1, x, y, z; i <= m; ++ i)
    		x = read(), y = read(), z = read(), add(x, y, z), add(y, x, 0);
    	EK();
    	printf("%d", ans);
    	return 0;
    }
    int read()
    {
    	int x = 0, f = 1;
    	char c = getchar();
    	while(c < '0' || c > '9'){if(c == '-') f = -1; c = getchar();}
    	while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + c - '0', c = getchar();
    	return x * f;
    }
    
  • 相关阅读:
    矩阵旋转
    clang-format 规范及 Visual Stido Code 自定义格式化代码风格
    Windows 安装 MinGW-w64
    Linux配置Visual Stdio Code
    Ubuntu中安装eclipse
    Ubuntu安装JDK11
    Ubuntu安装搜狗输入法
    C/C++算术运算(类型使用)的注意事项
    闰年判断与日期计算
    查看变量类型
  • 原文地址:https://www.cnblogs.com/Bn_ff/p/12398026.html
Copyright © 2011-2022 走看看