網絡最大流FF算法


題目描述

如題,給出一個網絡圖,以及其源點和匯點,求出其網絡最大流。

輸入輸出格式

輸入格式:

第一行包含四個正整數N、M、S、T,分別表示點的個數、有向邊的個數、源點序號、匯點序號。

接下來M行每行包含三個正整數ui、vi、wi,表示第i條有向邊從ui出發,到達vi,邊權為wi(即該邊最大流量為wi) 

輸出格式:

一行,包含一個正整數,即為該網絡的最大流。

輸入輸出樣例

輸入樣例#1:
4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 40
輸出樣例#1:
50

說明

時空限制:1000ms,128M

數據規模:

對於30%的數據:N<=10,M<=25

對於70%的數據:N<=200,M<=1000

對於100%的數據:N<=10000,M<=100000

樣例說明:

題目中存在3條路徑:

4-->2-->3,該路線可通過20的流量

4-->3,可通過20的流量

4-->2-->1-->3,可通過10的流量(邊4-->2之前已經耗費了20的流量)

故流量總計20+20+10=50。輸出50。

 

    求網絡最大流的算法還是很多的,這里講一下最簡單的FF算法。

還是利用增廣路,找到一條從源點到匯點的任意路徑,所有邊上的最小值delta如果不是0,

那么總流量就可以增加delta,在將路徑上的邊的容量減去delta,這就是一條增廣路。

    易知,如果找不出增廣路了,那么此時的流量就是最大了。

    但是,就這樣還不行,如果“走錯了”怎么辦?

    現在引進反向弧,反向弧可以解決這個問題。其他的博客上很多說反向弧可以讓流量后悔,

這個比喻很生動,但用我自己的話來說,反向弧因為是同原邊反向的,兩者中一者減去delta時,

將另一者加上delta,那么之后找增廣路時,走反向弧相當於原邊少走,非常巧妙。

    算法大概是將完了,代碼應該還好打的。

#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define INF 2147483647
using namespace std;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct edge{
    int next,to,head,b;
}f[440000];
long long ans;
int tot=0,n,m,s,t;
bool mark[400000]={0},flag;
void add(int u,int v,int w){
    f[tot].next=f[u].head;f[u].head=tot;f[tot].to=v;f[tot].b=w;tot++;
}
int dfs(int x,int flow){//dfs求任意路徑
    if(x==t){
        ans+=flow;
        flag=1;
        return flow;
    }
    mark[x]=1;
    for(int i=f[x].head;i!=-1;i=f[i].next){
        int x1=f[i].to;
        if(mark[x1]||f[i].b==0) continue;
        int delta=dfs(x1,min(flow,f[i].b));
        if(flag){
            f[i].b-=delta;
            f[i^1].b+=delta;
            return delta;
        }
    }
    return 0;
}
void FF(){//有增廣路就增廣
    memset(mark,0,sizeof(mark));
    flag=0;
    dfs(s,INF);
    while(flag){
        memset(mark,0,sizeof(mark));
        flag=0; 
        dfs(s,INF);
    }
}
int main(){
    n=read();m=read();s=read();t=read();
    for(int i=1;i<=n;++i) f[i].head=-1;
    while(m--){
        int x,y,w;x=read();y=read();w=read();
        add(x,y,w);add(y,x,0);
    }
    FF();
    printf("%lld",ans);
    return 0;
}

   注意,這種dfs的形式也是我自己很少打的,求任意一條路徑,

先dfs到匯點,再做一個標記flag,回來時有標記就更新。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM