1. 程式人生 > >網路流(最大流+模板)

網路流(最大流+模板)

 一、網路流的基本概念 先來看一個例項。 現在想將一些物資從S運抵T,必須經過一些中轉站。連線中轉站的是公路,每條公路都有最大運載量。如下圖: 每條弧代表一條公路,弧上的數表示該公路的最大運載量。最多能將多少貨物從S運抵T? 這是一個典型的網路流模型。為了解答此題,我們先了解網路流的有關定義和概念。 若有向圖G=(V,E)滿足下列條件: 1、有且僅有一個頂點S,它的入度為零,即d-(S) = 0,這個頂點S便稱為源點,或稱為發點。 2、有且僅有一個頂點T,它的出度為零,即d+(T) = 0,這個頂點T便稱為匯點,或稱為收點。 3、每一條弧都有非負數,叫做該邊的容量。邊(vi, vj)的容量用cij表示。 則稱之為網路流圖,記為G = (V, E, C) 譬如圖5-1就是一個網路流圖。 1.
可行流 對於網路流圖G,每一條弧(i,j)都給定一個非負數fij,這一組數滿足下列三條件時稱為這網路的可行流,用f表示它。 (1) 每一條弧(i,j)有fij≤cij。 (2) 除源點S和匯點T以外的所有的點vi,恆有: 該等式說明中間點vi的流量守恆,輸入與輸出量相等。 (3) 對於源點S和匯點T有: 這裡V(f)表示該可行流f的流量。 例如對圖5-1而言,它的一個可行流如下: 流量V(f) = 5。 2.可改進路 給定一個可行流f=。若fij = cij,稱<vi, vj>為飽和弧;否則稱<vi, vj>為非飽和弧。若fij = 0,稱<vi, vj>為零流弧;否則稱<vi, vj>為非零流弧。 定義一條道路P,起點是S、終點是T。把P上所有與P方向一致的弧定義為正向弧,正向弧的全體記為P+;把P上所有與P方向相悖的弧定義為反向弧,反向弧的全體記為P-。 譬如在圖5-1中,P = (S, V1, V2, V3, V4, T),那麼 P+ = {<S, V1>, <V1, V2>, <V2, V3>, <V4, T>} P- = {<V4, V3>} 給定一個可行流f,P是從S到T的一條道路,如果滿足: 那麼就稱P是f的一條可改進路。(有些書上又稱:可
增廣軌
)之所以稱作“可改進”,是因為可改進路上弧的流量通過一定的規則修改,可以令整個流量放大。具體方法下一節會重點介紹,此不贅述。 3.割切 要解決網路最大流問題,必須先學習割切的概念和有關知識。 G = (V, E, C)是已知的網路流圖,設U是V的一個子集,W = V\U,滿足S U,T W。即U、W把V分成兩個不相交的集合,且源點和匯點分屬不同的集合。 對於弧尾在U,弧頭在W的弧所構成的集合稱之為割切,用(U,W)表示。把割切(U,W)中所有弧的容量之和叫做此割切的容量,記為C(U,W),即: 例如圖5-1中,令U = {S, V1},則W = {V2, V3, V4, T},那麼 C(U, W) = <S, V2> + <V1, V2> + <V1, V3>+<V1, V4>=8+4+4+1=17 定理:對於已知的網路流圖,設任意一可行流為f,任意一割切為(U, W),必有:V(f) ≤ C(U, W)。 通俗簡明的講:“最大流小於等於任意割”。這是“流理論”裡最基礎最重要的定理。整個“流”的理論系統都是在這個定理上建立起來的,必須特別重視。 下面我們給出證明。 網路流、可改進路、割切都是基礎的概念,應該紮實掌握。它們三者之間乍一看似乎風馬牛不相干,其實內在聯絡是十分緊密的。 二、求最大流
何謂最大流?首先它必須是一個可行流;其次,它的流量必須達到最大。這樣的流就稱為最大流。譬如對圖5-1而言,它的最大流如下: 下面探討如何求得最大流。 在定義“可改進路”概念時,提到可以通過一定規則修改“可改進路”上弧的流量,可以使得總流量放大。下面我們就具體看一看是什麼“規則”。 對可改進路P上的弧<vi, vj>,分為兩種情況討論: 第一種情況:<vi, vj>∈P+,可以令fij增加一個常數delta。必須滿足fij + delta ≤ cij,即delta ≤ cij – fij。 第二種情況:<vi, vj>∈P-,可以令fij減少一個常數delta。必須滿足fij - delta ≥ 0,即delta ≤ fij 根據以上分析可以得出delta的計算公式: 因為P+的每條弧都是非飽和弧,P-的每條弧都是非零流弧,所以delta > 0。 容易證明,按照如此規則修正流量,既可以使所有中間點都滿足“流量守恆”(即輸入量等於輸出量),又可以使得總的流量有所增加(因為delta > 0)。 因此我們對於任意的可行流f,只要在f中能找到可改進路,那麼必然可以將f改造成為流量更大的一個可行流。我們要求的是最大流,現在的問題是:倘若在f中找不到可改進路,是不是f就一定是最大流呢? 答案是肯定的。下面我們給出證明。 定理1 可行流f是最大流的充分必要條件是:f中不存在可改進路。 證明: 首先證明必要性:已知最大流f,求證f中不存在可改進路。 若最大流f中存在可改進路P,那麼可以根據一定規則(詳見上文)修改P中弧的流量。可以將f的流量放大,這與f是最大流矛盾。故必要性得證。 再證明充分性:已知流f,並且f中不存在可改進路,求證f是最大流。 我們定義頂點集合U, W如下: (a) S∈U, (b) 若x∈U,且fxy<cxy,則y∈U; 若x∈U,且fyx>0,則y∈U。 (這實際上就是可改進路的構造規則) (c) W = V \ U。 由於f中不存在可改進路,所以T∈W;又S∈U,所以U、W是一個割切(U, W)。 按照U的定義,若x∈U,y∈W,則fxy = cxy。若x∈W,y∈U,則fxy = 0。 所以, 又因 v(f)≤C(U,W) 所以f是最大流。得證。 根據充分性證明中的有關結論,我們可以得到另外一條重要定理: 最大流最小割定理:最大流等於最小割,即max V(f) = min C(U, W)。 至此,我們可以輕鬆設計出求最大流的演算法: step 1. 令所有弧的流量為0,從而構造一個流量為0的可行流f(稱作零流)。 step 2. 若f中找不到可改進路則轉step 5;否則找到任意一條可改進路P。 step 3. 根據P求delta。 step 4. 以delta為改進量,更新可行流f。轉step 2。 step 5. 演算法結束。此時的f即為最大流。 下面給出一道題目便於理解,並附上程式碼
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<limits.h>
#include<math.h>
#include<queue>
#include<algorithm>
using namespace  std;
#define maxn 205
#define inf  0x7fffffff
int path[maxn][maxn];//記錄殘留網路的容量
int flow[maxn];//標記從原點到當前節點實際還剩多少流量
int pre[maxn];//標記在這條路徑上當前節點的前驅,同時標記該節點是否在佇列中
int n,m;
queue<int>q;
int bfs(int start,int end)
{
	while(!q.empty()) //清空佇列 
	    q.pop();
	memset(pre,-1,sizeof(pre));
	pre[start]=0;
	q.push(start);
	flow[start]=inf;
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		if(now==end)//找到了增廣路徑 
		break;
		for(int i=1;i<=m;i++)
		{
			if(i==now)
			  continue;
			if(path[now][i]>0 && pre[i]==-1)
			{
				pre[i]=now;
				flow[i]=min(path[now][i],flow[now]);
				q.push(i);
			}
		 } 
	}
	if(pre[end]==-1)//殘留網路中不存在增廣路 
	   return 0;
	else
	   return flow[end]; 
 } 
int maxflow(int start,int end)
{
	int ans;
	int sum=0;
	while((ans=bfs(start,end))!=0)
	{
		int k=end;//利用前驅尋找路徑 
		while(k!=start)
		{
			int last=pre[k];
			path[last][k]-=ans;
			path[k][last]+=ans;//正向最小流量等於方向最大流量(自己的理解) 
			k=last;
		}
		sum+=ans;
	}
	return sum;
}
int  main()
{
	int i,j;
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		memset(path,0,sizeof(path));
		memset(flow,0,sizeof(flow));
		int a,b,c;
		for(i=1;i<=n;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			if(a==b)//考慮起點和終點相同的情況 
			  continue; 
			path[a][b]+=c;//相同的起點和終點之間不止一條邊 
		}
		printf("%d\n",maxflow(1,m));
	}
 }