網路流 Dinic演算法理解

2022-07-31 03:21:12 字數 3821 閱讀 3270

ek演算法還是不夠優秀,我們還是要學習更加優秀的dinic演算法才能解決更多要求較高的問題。

這裡確保大家都是懂得網路流的一些基本概念的,如果不懂的,這裡有乙個鏈結,大家可以看一看。網路流詳解(顯然不是我寫的!)

他的ek演算法比我寫的好看的多,強勢給大家安利一波!!!

就是給你一次反悔的機會,類似於我們搜尋時改變當前層的乙個變數,後面又要把他改回來!!!

這裡**一下ek演算法和dinic演算法的一些演算法大體實現思想

在一般的增廣路演算法中, 程式的實現過程與增廣路求最大流的過程基本一致. 即每一次更新都進行一次找增廣路然後更新路徑上的流量的過程。但是我們可以從上圖中發現乙個問題, 就是每次找到的增廣路曲曲折折非常長, 此時我們往往走了冤枉路(即:明明我們可以從源點離匯點越走越進的,可是中間的幾條邊卻向離匯點遠的方向走了), 此時更新增廣路的複雜度就會增加。ek 演算法為了規避這個問題使用了 bfs 來尋找增廣路, 然後在尋找增廣路的時候總是向離匯點越來越近的方向去尋找下乙個結點。

借用一下**:

鄰接矩陣實現:

#include #include #include #include using namespace std;

const int maxn = 300;

const int max_int = ((1 << 31) - 1);

int n; // 圖中點的數目

int pre[maxn]; // 從 s - t 中的乙個可行流中, 節點 i 的前序節點為 pre[i];

bool vis[maxn]; // 標記乙個點是否被訪問過

int mp[maxn][maxn]; // 記錄圖資訊

bool bfs(int s, int t)}}

return false;}

int ek(int s, int t)

for(int i = t; i != s; i = pre[i])

ans += mi;

}return ans;

}

鄰接表實現:

const int maxn = 430;

const int max_int = (1 << 30);

struct edge;

struct node;

int n, m, ecnt;

bool vis[maxn];

int head[maxn];

node pre[maxn];

edge edge[maxn];

void init()

void addedge(int u, int v, int w)

bool bfs(int s, int t)}}

return false;}

int ek(int s, int t)

for(int i = t; i != s; i = pre[i].v)

ans += mi;

}return ans;}

// 加邊

addedge(u, v, w);

addedge(v, u, 0);

// 呼叫

int ans = ek(s, t);

每進行一次增廣需要的時間複雜度為 bfs 的複雜度 + 更新殘餘網路的複雜度, 大約為 o(m)(m為圖中的邊的數目), 需要進行多少次增廣呢, 假設每次增廣只增加1, 則需要增廣 nw 次(n為圖中頂點的數目, w為圖中邊上的最大容量), .

敲黑板演算法思想

dinic 在找增廣路的時候也是找的最短增廣路, 與 ek 演算法不同的是 dinic 演算法並不是每次 bfs 只找乙個增廣路, 他會首先通過一次 bfs 為所有點新增乙個標號, 構成乙個層次圖, 然後在層次圖中尋找增廣路進行更新。

演算法流程

利用 bfs 對原來的圖進行分層,即對每個結點進行標號, 這個標號的含義是當前結點距離源點的最短距離(假設每條邊的距離都為1),注意:構建層次圖的時候所走的邊的殘餘流量必須大於0

用 dfs 尋找一條從源點到匯點的增廣路, 注意: 此處尋找增廣路的時候要按照層次圖的順序, 即如果將邊(u, v)納入這條增廣路的話必須滿足, 其中 為結點 的編號。找到一條路後要根據這條增廣路徑上的所有邊的殘餘流量的最小值更新所有邊的殘餘流量(即正向弧 - l, 反向弧 + l).

重複步驟 2, 當找不到一條增廣路的時候, 重複步驟 1, 重新建立層次圖, 直到從源點不能到達匯點為止。

其實實現還是非常簡單的,下面放一篇本人ac的**,然後我再來講一講裡面要注意的一些細節。(注意上面是bfs找1條,只有1條!!!)

#includeusing namespace std;

const int m=100005;

const int inf=99999999;

struct sd

;sd edge[m*2];

vector next[m];

int cnt;

int layer[m];

void addedge(const int &from,const int &to,const int &flow)

; next[from].push_back(cnt++);

edge[cnt]=(sd);

next[to].push_back(cnt++);

}bool bfs(int start,int end)

}return layer[end];

}int dfs(int now,int end,int value)

return ret;

}int dinic(int start,int end)

return ans;

}int main()

printf("%d",dinic(st,en));

return 0;

}

我們最容易寫錯的東西其實就是dfs(),首先要注意就是每次dfs下去找最小的邊的時候,一定要保證最小邊不是0,如果是0,但是不判斷的話,他就會一直把乙個沒有用的標記進行上傳,非常的浪費時間。

最後如果增廣失敗不要忘記返回0(return 0),否則程式會出現奇怪的錯誤。

記住用鏈式前向星的與圖論的鏈式前向星的一些不同的地方!還有反向弧減邊的技巧。

ac code:

#includeusing namespace std;

const int inf =0x7fffffff;

struct sdedge[2005];

int n,m,cnt,head[2005],dep[2005],cur[2005],ans=0;

bool vis[2005];

void add(int a,int b,int c)

int bfs()

} }return dep[n];

}int dfs(int u,int w)

}} return 0;//? }}

int dinic()

return ans;

}int main()

printf("%d",dinic());

return 0;

}

網路流 演算法理解

交通網路中有 車流 貨流,供水網路中有水流,金融網路中有金流。這些都涉及到了最大流問題 而最大流存在於網路流中 那麼問題來了,什麼是網路流?有了這些,就構成了乙個基礎的網路流 有點繞。換個思路 自來水廠是源,家是匯 自來水廠和家之間有很多的管道用來輸送水。由於地形和地質的影響,管道規格有多種。通過的...

網路流dinic演算法

遇到過不少網路流的題目,直接找增廣路徑的方法時間複雜度實在受不了。常面臨tle的問題。通過學習這個dinic演算法,不僅 短,效率也高。該演算法的重點在於乙個層次圖,是在普通增廣的方法上加了優化,普通的增廣是每次在圖上四處遊蕩,直到找到匯點為止。dinic演算法就是把每個點都給乙個等級level l...

網路流Dinic演算法

我的模板 例題 struct edge edge int llst,int ffrom,int tto,int ccap,int fflow lst llst from from to tto cap ccap flow fflow dinic 演算法有3個重點 乙個是 層次圖 乙個是 阻塞流 乙個...