網絡流初步:<最大流>——核心(增廣路算法)
終於開始接觸網絡流了;
網絡流到底是個蝦米東東,用比較學術的話說,就是
一個有向圖 G=(V,E);
有兩個特別的點:源點s、匯點t;
圖中每條邊(u,v)∈E,有一個非負值的容量C(u,v)
記為 G=(V,E,C)
網絡三要素:點、邊、容量
用我的其中,最不好理解的就是容量和流量,對於初學者,應該這樣理解,每條邊有c表示最多能運送多少貨物,而流量f表示最多當前運送的貨物多少。
這樣要好點嘿嘿。
解決了網絡流之後,我們就要想,最大流。
what is最大流
在我們擁有網絡流的概念後,我們想啊,最大流,就是起點到終點的最大流量。
其中,在最大流的問題中,我們要滿足三個條件,1:容量限制:f(u,v)<c(u,v);
2:斜對稱性:(f(u,v)=-f(v,u))在後面的運用中,我們叫做一旦有物品從u運到v,那麽則肯定有一個可退流從v運到u
3:流量平衡:簡單來說就是除了源點和匯點,沒有其他點可以保存貨物。顯然f(s,u)==f(v,t);
既然這樣,我們的目的就是使f(s,u)和f(v,t)最大
怎麽使目標最大化呢,現在,就要介紹增廣路算法,這非常重要:可以說怎個網絡流都必須有增廣路算法的影子。
好,我們先想,如果一個網絡流還有一條增廣路,按照我們的理解,那麽這條路上所有的弧都可以讓x個貨物通過,那麽一定不是最大流
比如現在;
還存在S--A---C---T的可增廣路,那麽就一定不是最大流
但是反過來,如果一個網絡沒有增廣路,那麽他一定是最大流嗎,有的人說是的,當然啦。但是請看
這也是一個沒有增廣路的網絡,但是,他是最大流嗎。
我們發現,還可以這樣。orz
所以,剛才那個最多算一個極大流,而不是最大流(什麽蝦米東東)
也就是說從B出發,本來有兩條路可以走的,但是B卻走了錯誤的路,所以它成功地把C---T給堵住了。
我們又稱之為“阻塞流”。也就是說一個錯誤的路讓其他可行流被阻塞了。
怎麽改進呢,反正B出發只有兩條路,都要試一試對吧,既然上面那條是阻塞流。
那下面這條呢,試一試不就知道了,那我們就把B--c壓回去,走另外一條路。
現在。就是這樣
但是,這腫麽實現呢,如果模擬將流壓回去,忒難了點吧。
所以,我們引入了反向弧,借助他來推流。
增廣路徑(可改進路徑)的定義
若P是網絡中連結源點s和匯點t的一條路,我們定義路的方向是從s到t,則路上的弧有兩種:
l 前向弧---弧的方向與路的方向一致。前向弧的全體記為P+;
l 後向弧---弧的方向與路的方向相反。後向弧的全體記為P-;
設F是一個可行流,P 是從s到t的一條路,若P滿足下列條件:
在P+的所有前向弧(u,v)上,0≦f(u,v) < C(u,v);
在P-的所有後向弧(u,v)上,0<f(u,v) ≦C(u,v);
則稱P是關於可行流F的一條可增廣路徑。
有點難理解對吧。
但是我們成功了。
現在,網絡變成了這個樣子。
其實,為什麽反向弧退流就對了呢,我認為是這樣的,對於一條已經有了可退流的弧,一旦退流,其退流一定可以將退流退完,所以,滿足三要素。就一定是對的。
所以,增廣路定理就是:當一個殘量網路上沒有增廣路了,那麽他一定是最大流。
基於這個定理,我們可以設計出算法。
在一個殘量網絡上找增廣路增廣,然後,一旦沒有增廣路,就一定是最大流。
#include<cstdio> #include<cstring> #include<algorithm> #define N 10000+10 #define M 10000+10 #define inf 1e9; using namespace std; int head[N],arnum=1,used[N],ans,way[M]; struct ss{int next,to,cap;}a[M]; void add(int from,int to,int cap){a[++arnum]=(ss){head[from],to,cap};head[from]=arnum;} void insert(int u,int v,int cap){add(u,v,cap),add(v,u,0);} int n,m,S,T; void work(int step) { int minn=inf; for(int i=1;i<=step;i++) minn=min(minn,a[way[i]].cap); ans+=minn; for(int i=1;i<=step;i++) { a[way[i]].cap-=minn; a[way[i]^1].cap+=minn; } } int dfs(int u,int step) { for(int i=head[u];i;i=a[i].next) { int v=a[i].to; int cap=a[i].cap; if(not used[v] and cap>0) { way[step]=i; used[v]=1; if(v==T){work(step);return 1;} else if(dfs(v,step+1))return 1; } return 0; } } int main() { scanf("%d%d%d%d",&n,&m,&S,&T); int u,v,c; for(int i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&c);insert(u,v,c);} for(;;) { memset(used,0,sizeof(used)); used[S]=1; if(not dfs(S,1))break; } printf("%d",ans); return 0; }
這就是最低級的算法吧O(∩_∩)O哈哈~。
附上代碼
網絡流初步:<最大流>——核心(增廣路算法)