最大流問題的幾種模板和思路
阿新 • • 發佈:2018-11-06
核心思路:
(首先感謝網路大牛程式碼 附大牛網址:https://blog.csdn.net/huzhengnan/article/details/7766446)
我們首先 採用搜尋的方式 來找到一條從 s-t 的路徑 並且儲存記錄剩餘量,之後採用連結串列的方式記錄下我們走的每一條路,反向回去更新路的流量(正是有了反向流量 我們在搜素的時候 才能夠有一個"反悔"的機會)最終,找到重點,記錄最大流。
#include<iostream> #include<queue> using namespace std; const int N=201; const int INF=99999999; int n,m,sum;//s,t為始點和終點 int flow[N][N],cap[N][N],a[N],pre[N]; //分別為:flow[u][v]為<u,v>流量、cap[u][v]為<u,v>容量、a[i]表示源點s到節點i的路徑上的殘留量、pre[i]記錄i的前驅 void Edmonds_Karp(int s) { queue<int>q;//佇列,用bfs找增廣路 while(1) { memset(a,0,sizeof(a));//每找一次,初始化一次 a[s]=INF; q.push(s);//源點入隊 int sum=0; while(!q.empty()) { int u=q.front(); q.pop(); for(int v=1;v<=m;v++) { if(!a[v]&&flow[u][v]<cap[u][v]) { p[v]=u; q.push(v); a[v]=min(a[u],cap[u][v]-flow[u][v]);//s-v路徑上的最小殘量 } } } if(a[m]==0)//找不到增廣路,則當前流已經是最大流 break; sum+=a[m];//流加上 for(i=m;i!=s;i=p[i])// //從匯點順著這條增廣路往回走 { flow[p[i]][i]+=a[m];//更新正向流量 flow[i][p[i]]-=a[m];//更新反向流量 } } printf("%d\n",sum); } int main() { int v,u,w; while(scanf("%d%d",&n,&m)!=EOF) { memset(flow,0,sizeof(flow));//初始化 memset(cap,0,sizeof(cap)); while(n--) { scanf("%d%d%d",&u,&v,&w); cap[u][v]=w; } Edmonds_Karp(1); } return 0; }
目前我們很愉快的解決了一個基礎最大流問題,但是我們如果更加追求一下完美,讓整個程式佔得空間更加小一些,用的陣列更加小一些,是不是會更加優秀?
那麼我們的思路可以放在流量和容量這個問題上了,如果流量超過容量是不可以的,那麼我們流了多少,容量就相應的減去多少,是不是就會比我們增加“流量”這個數組裡的值要來的更加方便?
#include<iostream> #include<queue> using namespace std; const int N=201; const int INF=99999999; int n,m,sum,w;//s,t為始點和終點 int cap[N][N],a[N],p[N]; void Edmonds_Karp(int s) { int i,u,v; queue<int>q;//佇列,用bfs找增廣路 while(1) { memset(a,0,sizeof(a));//每找一次,初始化一次 a[s]=INF; q.push(s);//源點入隊 while(!q.empty()) { u=q.front(); q.pop(); for(v=1;v<=m;v++) { if(!a[v]&&cap[u][v]>0) { p[v]=u; q.push(v); a[v]=min(a[u],cap[u][v]);//s-v路徑上的最小殘量 } } } if(a[m]==0)//找不到增廣路,則當前流已經是最大流 break; sum+=a[m];//流加上 for(i=m;i!=s;i=p[i])// //從匯點順著這條增廣路往回走 { cap[p[i]][i]-=a[m];//更新正向流量 cap[i][p[i]]+=a[m];//更新反向流量 } } printf("%d\n",sum); } int main() { int v,u; while(scanf("%d%d",&n,&m)!=EOF) { sum=0;//記錄最大流量 memset(cap,0,sizeof(cap));//初始化 while(n--) { scanf("%d%d%d",&u,&v,&w); cap[u][v]=w; } Edmonds_Karp(1); } return 0; }
那麼就目前而看 基礎的最大流演算法我們已經搞得比較通透了。。但是這串程式碼只是個模板,還是要熟悉的運用。
總結: 最大流(用搜索實現,依靠的還有一些圖論的思想,通過有向圖反向地邊權值來實現一種“反悔”的方法)
當然這還不是最優秀的演算法。還可以先bfs對圖進行分層 之後再搜尋一下,會更省時間(下篇再見)