Dinic當前弧優化 模板及教程


在閱讀本文前,建議先自學最大流的Ek算法。

引入

Ek的核心是執行bfs,一旦找到增廣路就停下來進行增廣。換言之,執行一遍BFS執行一遍DFS,這使得效率大大降低。於是我們可以考慮優化。

核心思路

在一次BFS中,找到的增廣路可能不止一條,這時我們可以本着“盡量少進行BFS”的想法,在一次bfs后把所有能增廣的路徑全部增廣。
具體怎么做呢?
仍然是:
while(bfs(源點,匯點)) dfs();

每次bfs標記出每個點的“深度”,也就是距離源點的長度。我們將得到的新圖稱作分層圖。接下來我們在分層圖上進行增廣,把能增廣的路徑全部增廣。
其它部分和Ek大體相同。

關於當前弧優化

其實是一個很強的優化

每次增廣一條路后可以看做“榨干”了這條路,既然榨干了就沒有再增廣的可能了。但如果每次都掃描這些“枯萎的”邊是很浪費時間的。那我們就記錄一下“榨取”到那條邊了,然后下一次直接從這條邊開始增廣,就可以節省大量的時間。這就是當前弧優化

具體怎么實現呢,先把鏈式前向星的head數組復制一份,存進cur數組,然后在cur數組中每次記錄“榨取”到哪條邊了。

Code

//by floatiy
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN = 10000 + 5;
const int MAXM = 100000 + 5;
const int INF = 1e9;
int n,m;
int s,t;//源點 匯點
int maxflow;//答案
struct Edge {
    int next;
    int to,flow;
} l[MAXM << 1];
int head[MAXN],cnt = 1;
int deep[MAXN],cur[MAXN];//deep記錄bfs分層圖每個點到源點的距離
queue <int> q;
inline void add(int x,int y,int z) {
    cnt++;
    l[cnt].next = head[x];
    l[cnt].to = y;
    l[cnt].flow = z;
    head[x] = cnt;
    return;
}
int min(int x,int y) {
    return x < y ? x : y;
}
int dfs(int now,int t,int lim) {//分別是當前點,匯點,當前邊上最小的流量
    if(!lim || now == t) return lim;//終止條件
//  cout<<"DEBUG: DFS HAS BEEN RUN!"<<endl;
    int flow = 0;
    int f;
    for(int i = cur[now]; i; i = l[i].next) {//注意!當前弧優化
        cur[now] = i;//記錄一下榨取到哪里了
        if(deep[l[i].to] == deep[now] + 1 //誰叫你是分層圖
            && (f = dfs(l[i].to,t,min(lim,l[i].flow)))) {//如果還能找到增廣路
        flow += f;
            lim -= f;
            l[i].flow -= f;
            l[i ^ 1].flow += f;//記得處理反向邊
            if(!lim) break;//沒有殘量就意味着不存在增廣路
        }
    }
    return flow;
}
bool bfs(int s,int t) {
    for(int i = 1; i <= n; i++) {
        cur[i] = head[i];//拷貝一份head,畢竟我們還要用head
        deep[i] = 0x7f7f7f7f;
    }
    while(!q.empty()) q.pop();//清空隊列 其實沒有必要了
    deep[s] = 0;
    q.push(s);
    while(!q.empty()) {
        int tmp = q.front();
        q.pop();
        for(int i = head[tmp]; i; i = l[i].next) {
            if(deep[l[i].to] > INF && l[i].flow) {//有流量就增廣
            //deep我賦的初值是0x7f7f7f7f 大於 INF = 1e9)
                deep[l[i].to] = deep[tmp] + 1;
                q.push(l[i].to);
            }
        }
    }
    if(deep[t] < INF) return true;
    else return false;
}
void dinic(int s,int t) {
    while(bfs(s,t)) {
        maxflow += dfs(s,t,INF);
//      cout<<"DEBUG: BFS HAS BEEN RUN!"<<endl;
    }
}
int main() {
    cin>>n>>m;//點數邊數
    cin>>s>>t;
    int x,y,z;
    for(int i = 1; i <= m; i++) {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,0);
    }
//  cout<<"DEBUG: ADD FININSHED!"<<endl;
    dinic(s,t);
    printf("%d",maxflow);
    return 0;
}

 


免責聲明!

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



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