最大流的四种常用算法
最大流的四種常用算法
本博客僅用于記錄博主自己的代碼,只有代碼會有注釋,若是初學者想進行學習可以移步到最大流 — Edmond Karp算法里面主要講述了最大流的EK算法,另外的幾種算法也有推薦的大佬博客基本可以學會最大流算法。
最大流算法可以說有五種(FF , EK ,Dinic, ISAP, HLPP)
第一種是最暴力的dfs來實現的基本都會有超時的風險,所以不貼出代碼
基礎模板題:P3376 【模板】網絡最大流
進階毒瘤模板題:P4722 【模板】最大流 加強版 / 預流推進
值得一提的是前四種算法都是基于不斷尋找增廣路來實現的,而最后一個算法HLPP則是基于預流推進的原理來實現的,可以先學會前四種算法把模板過了后再去學習HLPP。
在進階毒瘤題中,在沒有各種玄學優化的情況下只有HPLL算法才能通過此題
嗯,歡迎各位大佬對我的代碼進行指點,糾錯,感謝!
第一種 Edmond Karp算法
#include<queue> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define ll long long const int N=205; const ll INF=1e18; int n,pre[N];//標記前繼節點 ll e[N][N];//存圖 bool vis[N]; bool bfs(int s,int t) {queue< int >q;memset(pre, -1, sizeof(pre));memset(vis, 0, sizeof(vis));pre[s] = s;vis[s] = 1;q.push(s);while(!q.empty()){int u=q.front();q.pop();for(int i=1;i<=n;i++){if(e[u][i]&&!vis[i]){vis[i]=1;pre[i]=u;if(i==t)return 1;q.push(i);}}}return 0; } ll EK(int s,int t) {ll maxflow = 0, d;//初始化為0流 while(bfs(s,t)) //bfs判斷是否還存在增廣路 {d = INF;//最值printf("%d\n",maxflow);for(int i=t;i!=s;i=pre[i]) //通過記錄的前繼節點向前訪問 d = min(d,e[pre[i]][i]);//記錄最小的delta for(int i=t;i!=s;i=pre[i])//更新容量 {e[pre[i]][i] -= d; //通過減小容量達到減小可過流量的目的 e[i][pre[i]] += d; //反向邊容量增加相同的大小 }maxflow += d; }return maxflow; } int main() {ll w;int i,m,s,t,u,v;scanf("%d%d%d%d",&n,&m,&s,&t);for(i=0;i<m;i++){scanf("%d%d%lld",&u,&v,&w);e[u][v]+=w; //有重邊時等于容量增加 }printf("%lld",EK(s,t));return 0; }許多大佬都是使用鏈式前向星來存圖的,單獨存放邊的容量,而我就不同了,奇奇怪怪思路清奇的用vector存邊,再用鄰接矩陣存邊權。
第二種 Dinic算法
#include <queue> #include <iostream> #include <string.h> #include <algorithm> using namespace std; #define ll long long const int N = 210, M = 5010; const ll inf = 1e18; int n,m,s,t,head[N],tot = 1;//注意要從1開始 即第一條邊存儲在e[2]中 struct edge {int to,nex,w; }e[M * 2]; void add(int from,int to,int w) {e[++ tot].w = w;e[tot].to = to;e[tot].nex = head[from];head[from] = tot; }int dep[N];//用bfs分層 bool bfs()//判斷是否還存在增廣路 {memset(dep,0,sizeof dep);queue<int>q;q.push(s);dep[s] = 1;while(!q.empty()){int u=q.front(); q.pop();for(int i = head[u]; i; i = e[i].nex){int v = e[i].to;if(e[i].w && !dep[v]){dep[v] = dep[u] + 1;q.push(v);}}}return dep[t]; }ll dfs(int u,ll inflow)//in為進入的流,源點的流無限大 {if(u == t)//到達匯點return inflow;//返回這一條增廣路的流量ll outflow = 0;for(int i = head[u]; i && inflow; i = e[i].nex)//還有殘余流量{int v = e[i].to;if(e[i].w && dep[v] == dep[u] + 1)//只搜索下一層次的點,防止繞回或走反向邊{ll flow = dfs(v , min(1ll * e[i].w,inflow));//min選取邊殘余容量與入流殘余的最小值e[i].w -= flow; //減去達到匯點的增廣流量e[i ^ 1].w += flow; //反向邊增加流量inflow -= flow; //入流減少相同的outflow += flow; //增廣路增加流量}}if(outflow == 0) dep[u]=0;//通過u點不能到達匯點,剪枝return outflow; }int main() {scanf("%d%d%d%d",&n,&m,&s,&t);for(int i=0;i<m;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);add(u,v,w);add(v,u,0);}ll ans = 0;while(bfs()) ans += dfs(s ,inf);printf("%lld",ans);return 0; }因為鏈式前向星存儲邊 正向反向邊的下標是相鄰的,初始化tot=1tot = 1tot=1使得所有正向邊的下標idxidxidx都是偶數,那么所有的反向邊 = idxXOR1idx XOR 1idxXOR1
第三種 ISAP算法
#include<queue> #include<vector> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define ll long long const int N=1205; const ll INF=1e18; int s,t,n,dep[N],gap[N];//dep點記錄深度,gap[i]記錄深度為i的點數量 ll e[N][N];//存邊權 vector<int>g[N];//存邊 void bfs() {//與其他增廣路算法不同之處在于bfs從匯點向源點跑dep[t]=1;gap[1]++;queue<int>q;q.push(t);while(!q.empty()){int u=q.front();q.pop();for(int i=0;i<g[u].size();i++){int v=g[u][i];if(!dep[v]){dep[v]=dep[u]+1;gap[dep[v]]++;q.push(v);}}} } ll dfs(int u,ll flow){if(u==t)return flow;ll used=0;for(int i=0;i<g[u].size();i++){int v=g[u][i];if(e[u][v]&&dep[v]+1==dep[u]){ll res=dfs(v,min(e[u][v],flow-used));e[u][v] -= res;e[v][u] += res;used += res;}if(used==flow)return used;//進入此點的流量全部用完直接返回}//前半段和Dinic一模一樣//如果已經到了這里,說明該點出去的所有點都已經流過了//并且從前面點傳過來的流量還有剩余//則此時,要對該點更改dep//使得該點與該點出去的點分隔開可以去去尋找另一條增廣路//從此點開始往前所有點的深度都會+1--gap[dep[u]];if(gap[dep[u]]==0) dep[s]=n+1;//出現斷層無法到達t了dep[u]++;//層數上升gap[dep[u]]++;return used; } int main() {int m,i,u,v;ll w,ans=0;scanf("%d%d%d%d",&n,&m,&s,&t);for(i=0;i<m;i++){scanf("%d%d%lld",&u,&v,&w);g[u].push_back(v), e[u][v]+=w;//+=重邊g[v].push_back(u);}bfs();while(dep[s]<=n)ans+=dfs(s,1e18);printf("%lld",ans);return 0; }第四種 HLPP算法
有時間的話補充…其實是我還沒學會
2022.09.08更新Dinic 模板(vector + 鄰接矩陣 -> 鏈式前向星)
2022.11.17網絡流練習題單與題解:網絡流練習題單
總結
以上是生活随笔為你收集整理的最大流的四种常用算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 识别操作系统
- 下一篇: tomcat8设置JAVA_HOME路径