网络流学习笔记

网络流

网络指\((Flow Network)\)有向图\(G= (V,E)\)
每条边都有权值\((x,y)\in E\)都有一个给定的值称为\(c(x,y)\),若\((x,y) \notin E\)\(c(x,y) = 0\),特别的有:\(S \in V\)\(T \in V(S \ne T)\),称为源点汇点
$G=(V, E) $是一个有向图,图中每条边 \((u, v) \in E\) 有一个非负的容量值$ c(u, v)=0$
而且,如果边集合 包含一条边 \((u, v)\), 则图中不存在反方向的边 \((v, u)\)

\(f(x,y)\)是定义在二元组\((x \in V , y \in V)\)的实数函数

  1. 容量限制:对于每条边,流经该边的流量不得超过该边的容量.\(f(u,v) \le c(u,v)\),若\(f(u,v) = c(u,v)\)则称为满流
  2. 斜对称性:每条边的流量与其相反边的流量之和为 0,即 \(f(u,v) = -f(v,u)\)
  3. 流守恒性:从源点流出的流量等于汇点流入的流量,即:\(\forall x\in V-\{s,t\},\sum_{(u,x)\in E}f(u,x)=\sum_{(x,v)\in E}f(x,v)\)

\(f\)称为\(G\)的流函数,\((u,v) \in E\),\(f(u,v)\)称为边的流量\(c(u,v)-f(u,v)\)称为边的剩余容量
整个网络的流量\(\sum_{(s,v \in E)}{f(s,v)}\)及从源点出发所有流量之和

网络流的相关概念

  1. 剩余流量\(c_f(u,v)\)表示这条边容量与流量之差\(c_f(u,v) = c(u,v)-f(u,v)\)
    假定有一个流网络 \(G=(V, E)\), 其源结点为 \(s\), 汇点为\(t\) 。设 \(f\)为图\(G\) 中的一·
    个流,考虑结点对 \(u, v \in V,\) 定义残存容量 \(c_f(u, v)\) 如下:

\[c_f(u,v)= \begin{cases} & f(u,v) - c(u,v) & (u,v) \in E \\ & c(v,u) & c(v,u) \in E \\ & 0 & 其他 \end{cases} \]

  1. 对于一个\(f\)流,残量网络\(G_f\):网络\(G\)中的节点和剩余流量大于\(0\)的边构成的子图

\[G_f=(V_f=V,E_f=\left\{(u,v)\in E,c_f(u,v)>0\right\}) \]

残量网络中包括了那些还剩了流量空间的边构成的图,也包括虚边
\(G_f\)类似一个流量为\(c_f\)的流网络,但该网络并不满足我们对流网络的定义,因
为它可能包含边\((u, v)\) 和它的反向边\((v, u)\)
3.设\(f\)为原图\(G\)的一个流 将边 \((u, v)\) 的流量增加 \(f'(u, v)\), 但减少 \(f'(v, u)\)
在残存网络中将流量推送回去也称为抵消操作 (cancellation)
4. 增广路径:在残量网络\(G_f\) 中,一条从源点汇点的路径被称为增广路
5. 对于\(G = (V,E)\)将点集\(V\)划分为 \(S,T=V-S\)两个集合,使\(s \in S,t \in T\),定义切割的净流量\(f(S,T)\)

\[f(S,T) = \sum_{u\in S}\sum_{v \in T} f(u,v)- \sum_{u\in S}\sum_{v \in T}f(v,u) \]

切割容量

\[c(S,T) = \sum_{u\in S}\sum_{v \in T}c(u,v) \]

最大流

我们有一张图,要求从源点流向汇点的最大流量(可以有很多条路到达汇点),就是我们的最大流问题。

Edmonds- Karp

定义。最大的流量问题是将尽可能多的流量从源头路由到汇,换句话说,找到流量\(f_{max}\)
若一条\(S \to T\)的路径上的每一条边都大于零,我们称它为增广路径,\(Edmonds- Karp\)便是通过\(BFS\)求增广路径来实现的。其复杂度为\(O(nm^2)\)

增广的时候要注意建造反向边,原因是这条路不一定是最优的,这样子程序可以进行反悔。假如我们对这条路进行增广了,那么其中的每一条边的反向边的流量就是它的流量。

当一条边的流量\(f(x,y)>0\)时,根据斜对称性质,它的反向边流量\(f(y,x)<0\),此时必定有\(f(y,x)<c(y,x)\),所以EK算法除了遍历原图的正向边以外还要考虑遍历每条反向边

算法步骤

  1. 我们就源点BFS ,碰到汇点就停,然后增广(每一条路都要增广)。
  2. 按照我们找的增广路在重新走一遍。走的时候把这条路的能够成的最大流量减一减,然后给答案加上最小流量就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N = 205,M = 100005,inf = 1 << 29;
typedef long long ll;
int h[N],ne[M],w[M],e[M],pre[N],incf[N],idx,t,s,n,m,maxflow;
bool st[N];
void add(int u,int v,int val)
{
	e[++idx] = v,w[idx] = val,ne[idx] = h[u],h[u] = idx;
}

int bfs()
{
	for(int i = 1 ; i <= n ; i ++ )st[i] = 0;
	queue<int>q;
	q.push(s),st[s] = 1;
	incf[s] = inf;//根据定义
	while(q.size())
	{
		int u = q.front();q.pop();
		for(int i = h[u];i;i=ne[i])
			if(w[i]){//标记
				int v = e[i];
				if(st[v])continue;
				incf[v] = min(incf[u],w[i]);//记录剩余容量最小值
				pre[v] = i;//记录方案
				q.push(v),st[v] = 1;
				if(v == t)return 1;//找到汇点
			}
	}
	return 0;
}

void update()
{
	ll x = t;
	while(x!=s)
	{
		int i = pre[x];
		w[i] -=incf[t];
		w[i^1] += incf[t];
		x = e[i^1]; 
	}
	maxflow += incf[t];
}

int main()
{
	cin >> n >> m >> s >> t;
	idx = 1,maxflow = 0;
	for(int u,v,val,i = 1 ; i <= m ; i ++ )
	{
		scanf("%d%d%d",&u,&v,&val);
		add(u,v,val);
		add(v,u,0);
	}
	while(bfs())update();
	cout << maxflow <<  endl;
		
}

Dinic

由于每次\(ET\)算法都要遍历整个残量网络,找到一条增广路径,还有进一步优化的空间。设\(d[x]\)表示从\(S\)\(x\)最少需要经过的边数在残量网络中满足\(d[y] = d[x] +1\)的边\((x,y)\)构成的子图被称为分层图

  1. 在残量网络上BFS求节点的层次,构造分层图
  2. 在分层图上BFS寻找增广路在回溯时更新剩余流量。
#include <bits/stdc++.h>

using namespace std;

const int N = 2e3 + 5,M = 5e5 + 5,inf = 1<<29;
int n,m,idx = 1,ne[M],h[N],e[M],w[M],d[N],s,t;
long long maxflow;
queue<int>q;
void add(int x,int y,int z)
{
    e[++idx] = y,ne[idx] = h[x],w[idx] = z,h[x] = idx;
}

bool bfs()
{
    memset(d,0,sizeof (d));
    while(q.size())q.pop();
    d[s] = 1,q.push(s);
    while(q.size())
    {
        int x = q.front();q.pop();
        for(int i = h[x] ;i ; i = ne[i] )
            if(w[i] && !d[e[i]])
            {
                q.push(e[i]);
                d[e[i]] = d[x] + 1;
                if(e[i] == t)return 1;
            }
    }
    return 0;
}


int dinic(int x,int flow)
{
    if(x == t)return flow;
    int res = flow,k;
    for(int i = h[x]; i && res ; i = ne[i])
        if(w[i] && d[e[i]] == d[x] + 1)
        {
            k = dinic(e[i],min(res , w[i]));
            if(!k)  d[e[i]] = 0;
            w[i] -= k;
            w[i^1] += k;
            res -= k;
        }
        return flow - res;
}

int main()
{
    cin >> n >> m >> s >> t;
    idx = 1;
    for(int x,y,z,i = 1 ; i <= m ; i ++ )
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,0);
    }
    int flow = 0;
    while(bfs())
        while (flow = dinic(s,inf))maxflow += flow;

    cout << maxflow << endl;
        
}
posted @ 2022-07-26 20:56  Erfu  阅读(119)  评论(0)    收藏  举报