網路流基礎演算法模板

2021-08-04 07:12:01 字數 2764 閱讀 8368

網路流是一種非常玄妙的演算法,被廣泛地用於各種有權值存在或一對多的匹配問題中。而網路流又有許多數學性質,比如最大流等於最小割等等。本篇主要介紹常用的dinic最大流演算法。

網路,就是一張有點有邊圖。其中有兩個特殊的點——源點和匯點。網路流中的每一條邊就好比一條水管,容量就好比是這個水管的粗細。我們要求的最大流,指的就是從源點開始往圖里注水,水會沿著網路流動,然後從匯點流出。因為每一條邊的容量一般都有限,而又因為網路流中每個點,流入它的的「水量」等於流出它的的「水量」,所以以這個從源點注入的水流的最大流量一定是有限的,否則這個網路是承載不了的。這個最大的流量就叫「最大流」。

再說說什麼是割。割就是堵死網路流中的一些水管,使得這個圖中的源點和匯點不再連通。水管可以被完全堵死,也可以被堵死一部分(或者你也可以把它理解為,割掉圖中的一些邊)。當然了,割掉一些邊也是要花費一定代價的。割掉一條容量為x的邊,花費的代價就為x。在所有的「割」中,花費代價最少的一種「割法」所花費的代價就叫做「最小割」。

dinic演算法被用來求最小割。首先,要說求最小割,我的第一種思路就是去從起點「阻塞」當前的殘量網路,一直阻塞到不能阻塞為止。這種方法有一定概率得到正確答案,但是由於「貪心不總是對的」,所以有的時候這種方法會「割多」。怎麼處理這種情況呢?dinic演算法利用了一種「反向邊」的思想解決了這個問題。

首先我們先明確一下邊的概念:

struct edge

};

這裡的邊都是有向邊,這個結構體表示一條從from流向to,容量為cap,當前已經被占用的流量為flow的邊。(即殘量為cap-flow,殘量大於0說明這條邊仍在網路流中。如果cap-flow=0,說明這條邊已經被完全割掉)

dinic演算法在把正向邊加入圖中的同時又向圖中加入了它的反向邊。反向邊與正向邊方向相反(即從to到from),而且反向邊的容量cap為0。每當我要在一條邊e上割掉f的流量時,我們要做兩個操作,一是把e.flow+=f,而是把e的反向邊nege的流量nege.flow-=f。為什麼要這樣呢?

因為nege為反向邊,nege.cap一定等於零,如果nege.flow=-f(f>0)。那麼nege.cap-nege.flow=f>0。這條「反邊」就會像正邊一樣被看成是殘量網路中的邊。假設我下一次又走了這條「反邊」,那麼就會讓nege.flow+=f,e.flow-=f(nege和e互為反向邊)。就相當於是撤銷了上一次對邊e的割。這就使得如果乙個割不是最優的,那麼它中的一些被割的邊一定是可被恢復的,這就彌補了貪心的不足。

假如我在這個圖中走了上圖中的紅邊,這是三條紅邊的反向邊也會被加入到圖中。

繼續沿著紅邊走,因為中間那條綠邊的反向邊被加入到了圖中,所以可以走。走完之後綠邊被還原。

而實際上,上面走的兩條紅邊的效果相當於走了這兩條藍邊(因為,中間的那條邊被還原了)。所以「還原」一條邊的實質是選擇了其他的割的方案。

當然圖裡邊也不是想怎麼走怎麼走的,水往低處流,而不流回頭路。你可以把這理解為,把這個網路流中的點分成一些層次,每次都只能從乙個層次的點走到下乙個層次的點。這個層次就是從起點走到某乙個點最少要經過的邊數(這裡的邊數指的是殘量網路中的邊數,所以每一次増廣都要重新分層)。這個分層的工作可以用乙個簡單的bfs實現。

然後我們給出這個模板:

#include

#include

#include

#include

using

namespace

std;

const

int maxn=10001,inf=0x7fffffff;

struct edge//表示一條邊

};int min(int a,int b)

bool bfs()//分層bfs }}

return vis[t];//如果匯點沒被訪問

//說明從原點到匯點之間已不存在增廣路,不可増廣

/上文中的d[i]表示從s到i最少經過的邊數

}int dfs(int x,int a)//x表示當前點,a表示當前弧上的最小殘量

}return flow;

}int maxflow(int s,int t)

return flow;

}}dinic;

這個dfs函式是網路流的精髓,它表示我當前已經走到了結點x,當前經過的最小的乙個邊的容量為a,所能產生的最大的流量。令e為x的所有出邊,就有dfs(x,a)=∑dfs(e.to,min(m,e.cap-e.flow)),因為點x流入流量等於a,所以流出的流量和也就是∑m=a。用貪心的想法,每次在a的身上減去dfs(e.to,min(a,e.cap-e.flow)),這樣剩餘流出量就會越來越少,直到等於0就break(這是用來提速的)。如果貪心不正確,那麼它在接下來的貪心之中還會被「貪回來」。

bfs函式用來分層,返回的值是匯點t是否被訪問過。如果匯點t沒有被訪問過就說明從源點到匯點已經沒有任何一條増廣路徑了,演算法就可以終止。起點沒有前驅結點所以它的前驅路徑上的最小邊權就為正無窮,所以我們每次可以對起點進行a=+∞的dfs,然後把這些得數累加起來,直到bfs=false為止。

趕稿匆忙,如有謬誤,望同學們理解。

宣告:之前版本的dinic**有bug,望諒解,現已重新整理。——2017.8.8

網路流演算法模板

引用於一些大佬的文章 dinic演算法 第二個模板為白書的寫法 poj1273為例 題意 m條路徑,n個點,每條路徑輸入起點,終點和流量,求源點到匯點的最大流量 include include include include define inf 9999999 using namespace st...

網路流演算法模板

1 基於ford fulkerson方法的edmonds karp演算法 用廣度有限搜尋來實現對增廣路徑p 的計算。即,如果增廣路徑是殘留網路種從s 到t 的最短路徑,則能夠改進ford fulkerson的界。view code 1 做一次增廣路徑的流量統計23 int augment else ...

網路流EdmondsKarp演算法模板理解

先推薦乙個講網路流的部落格,我的網路流知識均吸收於此 傳送門 edmondskarp演算法基本思想 從起點到終點進行bfs,只要存在路,說明存在增廣路徑,則取這部分路 權值最小的一部分,即為增廣路徑 也就是這一部分路的最大流量 然後將這條路上的正向權值都減去min,反向權值都加上min 即,m i ...