Dinic當前弧優化 模板及教程

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

引入

Ek的核心是執行bfs,一旦找到增廣路就停下來進行增廣。換言之,執行一遍BFS執行一遍DFS,這使得效率大大下降。因而咱們能夠考慮優化。算法

核心思路

在一次BFS中,找到的增廣路可能不止一條,這時咱們能夠本着「儘可能少進行BFS」的想法,在一次bfs後把全部能增廣的路徑所有增廣。
具體怎麼作呢?
仍然是:
while(bfs(源點,匯點)) dfs();數組

每次bfs標記出每一個點的「深度」,也就是距離源點的長度。咱們將獲得的新圖稱做分層圖。接下來咱們在分層圖上進行增廣,把能增廣的路徑所有增廣。
其它部分和Ek大致相同。優化

關於當前弧優化

實際上是一個很強的優化spa

每次增廣一條路後能夠看作「榨乾」了這條路,既然榨乾了就沒有再增廣的可能了。但若是每次都掃描這些「枯萎的」邊是很浪費時間的。那咱們就記錄一下「榨取」到那條邊了,而後下一次直接從這條邊開始增廣,就能夠節省大量的時間。這就是當前弧優化code

具體怎麼實現呢,先把鏈式前向星的head數組複製一份,存進cur數組,而後在cur數組中每次記錄「榨取」到哪條邊了。blog

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;
}
相關文章
相關標籤/搜索