網路流與費用流(上)網路流

2021-07-10 21:40:22 字數 2835 閱讀 7787

建圖,每條邊要建反向邊,容量為0(無向圖容量跟正向邊一樣),一直找從s到t的增廣路,每次找到都把最小的容量加到最大流,然後路上所有邊都減去這個值,對應的反向邊加上這個值,知道找不到增廣路。

ff方法的最簡單實現,每次尋找增廣路都是樸素的bfs。時間複雜度o(n*m^2)。

sap演算法使用距離標號的最短增廣路,距離標號的思想**於push_relable類演算法(不知道是啥)。

演算法思路:

所謂距離標號就是最少經過多少條有容量的邊能到達匯點,假如距離標號陣列為lv,找增廣路找到u點的時候都按照lv[u]==lv[v]+1找出v點擴充套件,滿足這個條件的邊稱為可行弧,證明什麼的就不說了。

距離標號可以在演算法開始之前進行一次bfs把初始標號預處理出來,這樣有利於常數上的優化,也可以不預處理先直接清0後面再維護。關於距離標號的維護,由於增廣時都走容量大於0的可行弧,如果沒有容量大於0的邊了那這個點差不多就廢了,距離標號為n用於斷層優化;如果還有容量大於0的邊但沒有可行弧那u的lv就取可擴充套件點的最小lv+1。

sap可以加很多常數上的優化,優化全加上後效果似乎很好,比如上面的bfs預處理。除了這個以外還有三個很重要的優化就是當前弧優化,斷層優化,多路增廣優化。

模板**:

const int maxn=100010;

const int maxm=maxn<<1;

const int inf=0x3f3f3f3f;

int gap[maxn],lv[maxn],pre[maxn],cur[maxn];

struct edge

e[maxm];

int h[maxn],ecnt;

inline void init()

inline void addedge(int u,int v,int w)

inline void addedge(int u,int v,int w)

int sap(int n,int s,int t) }

while(lv[s]

hdu4280測速結果,這個是我自己進行終極優化考慮各種情況的版本了= =,搞了一天都**了,這個時間還是比較滿意的

其實網路流演算法萬變不離其宗,dinic的思路還是跟sap差不多的,只不過實現上的差異讓dinic也有優秀的時間表現。

演算法思路:

每次增廣之前都從源點開始bfs一次,這次是搞相對於源點的距離標號,一旦訪問到匯點就說明可以增廣,結束bfs並開始增廣,如果bfs結束後都沒有訪問到匯點,那就不能再增廣了,演算法結束。增廣則是使用dfs進行多路增廣,實現起來十分方便給力。

模板**:

const int maxn=210;

const int maxm=maxn<<1;

const int inf=0x3f3f3f3f;

int lv[maxn];

struct edge

e[maxm];

int h[maxn],ecnt;

inline void init()

inline void addedge(int u,int v,int w)

inline void addedge(int u,int v,int w)

bool bfs(int s,int t)

} return 0;

}int dfs(int u,int flow,int t)

} return ret;

}int dinic(int s,int t)

以後遇到時間要求不高的題就寫dinic,因為實在是很好寫,有什麼意外調起來也好些;時間限制比較緊張的就寫極度優化的sap,雖然我沒自己測試過這個跟dinic到底哪個比較快,但既然dinic都tle了那就試試這個唄= =感覺要被壓倉底了

這個意思就是邊不僅有容量上限而且還有下限,這時候就要先確定能不能滿足下限的要求,也就是所謂的先求可行流。

首先對於原來的每條邊(u,v),都把它的容量建成上限減下限。然後加上超級源匯點,對於每個點u都記一下所有(i,u)邊的下限的和記為in[u],所有(u,i)邊的下限和記為out[u],算一下tmp=in[u]-out[u],如果tmp為0就不用管它了,如果tmp大於0那麼就從超級源點向u點連一條容量為tmp的邊,否則就從u向超級匯點連一條容量為-tmp(因為tmp此時為負值)。然後跑一遍最大流,跑完檢查一下超級源點的所有出邊的容量是否都用完,沒全部用完說明沒有可行流;如果存在可行流,那麼這個可行流方案中原來的每條邊的流量就是它的下限加上剛剛跑完最大流之後對應那條邊所用的流量,這個可以在回退邊找到。

至於正確性我在這裡就不詳細說了,隨便一搜都一大把,主要說的就是根據每個點流量平衡的原則對網路進行變形,各種邊的關係加加減減啊什麼的。

大家都知道對於有源匯點的網路來說源點的流出量是等於匯點的流入量的,那我們只要連一條從t到s的容量無窮大的邊就把網路變成上面的無源匯情況了,於是還是用上面的方法先確定可行流的存在。根據流量平衡可知s到t的可行流被儲存在邊t->s上記為f,然後我們只要把這個邊去掉,在殘餘網路從s到t求一遍最大流得到f『,那麼f+f'就是滿足下限的最大流了。

然而這種問題的出得比較多的還是最小流。但最小流其實也思想也查不多,具體做法是:在加t->s邊之前先從跑一遍超級s到超級t的最大流,這一步把除了最小流的部分流走,然後再加上容量為inf的t->s邊,再從超級s到超級t跑一邊最大流,此時t->s的流量就是答案。

另外這兩種問題還有另外一種比較好理解的做法就是,拿最大流來說,把t->s的邊建成上限為inf,下限為x的邊,然後二分這個x,直到找到最大的x滿足存在可行流的條件,而最小流顯然就是二分上限了。這種做法雖然比較好想,但是在時間上多個log,實現起來也挺麻煩的(因為每次求可行流都要恢復原網路),一般還是採用上面的方法比較好(雖然上面的實現起來也簡單不到**去就是了= =)。

網路流 費用流

這個好像不考 沒事可以騙分 費用流,顧名思義,就是有費用的流,也就是說,給乙個網路流圖中的每條弧增加乙個單位流量費用。一般來說求解的費用流都是最大流最小費用。好像沒什麼好bb的 這裡推薦使用zkw演算法求解最小費用流,看著 理解就行,應該還是很好理解的。zkw演算法在稠密圖上跑得飛快,在稀疏圖上還不...

網路流 費用流

網路流有很多種類 其中最大流 有增廣路演算法和預流推進演算法。增廣路演算法就是不斷的新增增廣路。其中的dinic演算法。會稍微提到isap演算法 poj1273 首先想到dfs一直往後延伸,然後從源點到匯點計算每條路,但是這樣只是單條路的最值,有時可能因為走一條路而間接的認定了除這條路以外的某個路通...

網路流之費用流

求費用流目前好像只有ek spfa改版,時間複雜度為o n e k 其中k為最大流值。但時間上的期望時間複雜度為 o a e k 其中a為所有頂點進佇列的平均次數,可以證明a一般小於等於2。最小費用最大流 include using namespace std const int inf 0x3f3...