1. 程式人生 > >ACM—網路流初步

ACM—網路流初步

本圖示最大流的一個例項。由此,可以引出最大流的一些基本的定義和概念

可以這樣看,圖就是一種管道,管道有最大通過流量的限制,圖中邊的權值就是所謂的“容量”。同時,注意有唯一的源點和匯點。

這裡需要注意容量和流量的區別。其中f(u,v)的範圍需要額外注意,是 0<= f(u,v) <= c(u,v),不會出現所謂的負流量。下圖是對可行流的圖示

有了可行流,我們還需要求最大流

那麼如何求最大流呢。可以採用著名的Ford Fulkerson演算法

所以說,演算法的關鍵在於

1)何為增廣路徑,如何找出增廣路徑。

2)如何更新流量

說的直白些,所謂增廣路徑,就是找到這樣一條路徑,其流量不滿,未達到容量上限。

所有的可能的增廣路徑在一起便構成了殘留網路。

那麼,如何增廣呢。

其實,這裡的這個描述不太準確。下面我根據我的理解再解釋一下。

第一步,計算可增加流量

設某一增廣路徑上的節點為(a1,a2,a3,a4,....,an)

如果(u,v)是正向邊,則增加流量d = min{ c(ai,aj) - f(ai,aj) | j = i +1, i =1,2,3...,n-1}

如果是逆向邊,則增加流量d = min{ f(ai, aj) | j = i +1, i =1,2,3...,n-1}

第二步,更新流量

如果(u,v)是正向邊,則 f(u,v) = f(u,v) + d

是逆向邊,則f(u,v) = f(u,v) - d

注意,如果是逆向邊,就是減法,當前管道從中減去部分流量,而且,伴隨著這部分減去的流量,必有另一部分管道的流量會增加。。而且,最後的總流量增加了d

結合上述演算法,可以詳細參閱下下列圖示

可以證明,可行流為最大流,當且僅當不存在新的增廣路徑。

總結一下最大流演算法

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 201;

int edges[maxn][maxn];
int n,m;
int ans;
int father[maxn];
bool visited[maxn];

void ford_fulkerson()
{
    ans = 0;
    while(1)
    {
        queue<int> Q;
        memset(father,-1,sizeof(father));
        memset(visited,0,sizeof(visited));
        visited[0] = true;
        Q.push(0);
        while( !Q.empty())
        {
            int now = Q.front();
            Q.pop();
            if(now == m-1)
                break;
            for(int i = 0; i < m; i++)
            {
                if(!visited[i] && edges[now][i])
                {
                    father[i] = now;
                    visited[i] = true;
                    Q.push(i);
                }
            }
        }
        if( !visited[m-1] )
            break;
        int minimal = 1000000000;
        for(int i = m-1; i ; i = father[i])
        {
            if(minimal > edges[father[i]][i])
                minimal = edges[father[i]][i];
        }
        for(int i = m-1; i ; i = father[i])
        {
            edges[father[i]][i] -= minimal;
            edges[i][father[i]] += minimal;
        }
        ans += minimal;
    }
    printf("%d\n",ans);
}
int main()
{
    freopen("input.txt","r",stdin);
    while(~scanf("%d%d",&n,&m))
    {
        int f,t,w;
        memset(edges,0,sizeof(edges));
        for(int i = 0; i < n; i++)
        {
            scanf("%d%d%d",&f,&t,&w);
            edges[f-1][t-1] += w;
        }
        ford_fulkerson();
    }
    return 0;
}