1. 程式人生 > >網絡流初步:<最大流>——核心(增廣路算法)

網絡流初步:<最大流>——核心(增廣路算法)

dfs space 10000+ can style 最大 strong names using

終於開始接觸網絡流了;

網絡流到底是個蝦米東東,用比較學術的話說,就是

一個有向圖 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哈哈~。

附上代碼

網絡流初步:<最大流>——核心(增廣路算法)