網絡流Dinic算法


是什么是網絡流

在一個有向圖上選擇一個源點,一個匯點,每一條邊上都有一個流量上限(以下稱為容量),即經過這條邊的流量不能超過這個上界,同時,除源點和匯點外,所有點的入流和出流都相等,而源點只有流出的流,匯點只有匯入的流。這樣的圖叫做網絡流。

一些定義

源點:只有流出去的點
匯點:只有流進來的點
流量:一條邊上流過的流量
容量:一條邊上可供流過的最大流量
殘量:一條邊上的容量-流量

網絡流最大流

定義

網絡流的最大流算法就是指的一個流量的方案使得網絡中流量最大。

網絡流最大流求解

網絡流的所有算法都是基於一種增廣路的思想,下面首先簡要的說一下增廣路思想,其基本步驟如下:

  1. 找到一條從源點到匯點的路徑,使得路徑上任意一條邊的殘量>0(注意是小於而不是小於等於,這意味着這條邊還可以分配流量),這條路徑便稱為增廣路
  2. 找到這條路徑上最小的F[u][v](我們設F[u][v]表示u->v這條邊上的殘量即剩余流量),下面記為flow
  3. 將這條路徑上的每一條有向邊u->v的殘量減去flow,同時對於起反向邊v->u的殘量加上flow(為什么呢?我們下面再講)
  4. 重復上述過程,直到找不出增廣路,此時我們就找到了最大流

這個算法是基於增廣路定理(Augmenting Path Theorem): 網絡達到最大流當且僅當殘留網絡中沒有增廣路.

為什么要建反邊

舉個栗子:
先找增廣路,比如說我們走了s->3->5->t,那么圖會變成:
在這里插入圖片描述
再找一次增廣路,這次比如說我們走s->4->5->t。
在這里插入圖片描述
(那個65被洛谷打了美觀的水印,將就看吧)

再找一次增廣路,這次我們找s->4->5->3->t
在這里插入圖片描述

好像有點奇怪。你可能會想:為什么這次搜索的時候走了反向邊(紅色)的邊,為什么走了反向邊(紅色)的邊會加正向邊(黑色)的邊?

感性理解一下,就好像有45個人通過s->4->5->t到達匯點。
但是到5點時,我們發現有十個人來自3點,兩股人流無法通過5->t,
於是我們通過反邊發現3點來的人可以直接3->t到達匯點,這樣安排,
就可以使得人流量最大。

那反邊的意義就是:
我們知道,當我們在尋找增廣路的時候,在前面找出的不一定是最優解,如果我們在減去殘量網絡中正向邊的同時將相對應的反向邊加上對應的值,我們就相當於可以反悔從這條邊流過。
比如說我們現在選擇從u流向v一些流量,但是我們后面發現,如果有另外的流量從p流向v,而原來u流過來的流量可以從u->q流走,這樣就可以增加總流量,其效果就相當於p->v->u->q

簡單來說就是:
留下一個標記,讓后面的人有機會讓前面的人走另一條路。

朴素算法的低效之處

雖然說我們已經知道了增廣路的實現,但是單純地這樣選擇可能會陷入不好的境地,比如說這個經典的例子:
在這里插入圖片描述
我們一眼可以看出最大流是999(s->v->t)+999(s->u->t),但如果程序采取了不恰當的增廣策略:s->v->u->t
在這里插入圖片描述
我們發現中間會加一條u->v的邊
在這里插入圖片描述
下一次增廣的時候:
在這里插入圖片描述
若選擇了s->u->v->t
此處輸入圖片的描述

然后就變成
此處輸入圖片的描述

這是個非常低效的過程,並且當圖中的999變成更大的數時,這個劣勢還會更加明顯。

怎么辦呢?

這時我們引入Dinic算法

為了解決我們上面遇到的低效方法,Dinic算法引入了一個叫做分層圖的概念。具體就是對於每一個點,我們根據從源點開始的bfs序列,為每一個點分配一個深度,然后我們進行若干遍dfs尋找增廣路,每一次由u推出v必須保證v的深度必須是u的深度+1。

Dinic算法具體步驟如下:
  1. 初始化容量網絡和網絡流。
  2. 構造殘留網絡和層次網絡,若匯點不再層次網絡中,則算法結束。
  3. 在層次網絡中用一次DFS過程進行增廣,DFS執行完畢,該階段的增廣也執行完畢。
  4. 轉步驟2。

下面給出Dinic算法的代碼:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#define ll long long
#define inf 0x3f3f3f3f

using namespace std;

const ll N = 2e5 + 10;
struct node {ll to, next, w;} e[N]; 
ll n, m, s, t, u, v, w, cnt = 1, head[N], maxflow, d[N], now[N];

void add(ll x, ll y, ll z)
{
	e[++cnt] = (node){y, head[x], z};
	head[x] = cnt;
}

bool bfs()
{
	queue <ll> q;
	memset(d, 0, sizeof(d));
	while(!q.empty()) q.pop();
	q.push(s), d[s] = 1, now[s] = head[s];
	while(!q.empty())
	{
		ll x = q.front(); q.pop();
		for(ll i = head[x]; i; i = e[i].next)
		{
			ll v = e[i].to;
			if(e[i].w && !d[v])
			{
				d[v] = d[x] + 1;
				q.push(v);
				now[v] = head[v];
				if(v == t) return 1;
			}
		}
	}
	return 0;
}

ll dinic(ll x, ll flow)
{
	if(x == t) return flow;
	ll rest = flow, i, k;
	for(i = now[x]; i && rest; i = e[i].next)
	{
		ll v = e[i].to;
		if(e[i].w && d[v] == d[x] + 1)
		{
			k = dinic(v, min(rest, e[i].w));
			if(!k) d[v] = 0;
			e[i].w -= k;
			e[i ^ 1].w += k;
			rest -= k;
		}
	}
	now[x] = i;
	return flow - rest;
}

int main()
{
	scanf("%lld%lld%lld%lld", &n, &m, &s, &t);
	for(ll i = 1; i <= m; i++)
		scanf("%lld%lld%lld", &u, &v, &w), add(u, v, w), add(v, u, 0);
	while(bfs())
	 while(ll res = dinic(s, inf)) 
		maxflow += res;
	printf("%lld\n", maxflow);
	return 0;
}

題目來源:link


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM