图论 —— 网络流 —— 最大流 —— Dinic 算法
生活随笔
收集整理的這篇文章主要介紹了
图论 —— 网络流 —— 最大流 —— Dinic 算法
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【概述】
Dinic 算法在 EK 算法的基礎上進行了優化,其時間復雜度為 O(n*n*m)。
Dinic 在找增廣路的時也是找最短增廣路, 但與 EK 算法不同的是 Dinic?算法并不是每次 bfs 只找一個增廣路,他會首先通過一次 bfs 為所有點添加一個標號,構成一個層次圖,然后在層次圖中尋找增廣路進行更新。
【基本思想】
1.初始化容量網絡與網絡流
2.構造殘量網絡,根據殘留網絡通過 BFS 計算層次網絡,若匯點不在層次圖中(匯點層次為 -1),則結束
3.在層次網絡中使用一次 DFS 進行增廣,DFS 執行完畢,該階段的增廣也執行完畢
4.轉步驟 2
【求最大流】
1.一般方式
使用鄰接矩陣的形式,適用于點較少的情況。
int n,m;//n個點m條邊 int S,T;//源點、匯點 int level[N];//存儲結點分層層數 struct Edge{int cap;int flow; }edge[N][N]; bool bfs(){//構造層次網絡memset(level,0,sizeof(level));queue<int> Q;Q.push(S);level[S]=1;while(!Q.empty()){int x=Q.front();Q.pop();for(int y=1;y<=n;y++){if(!level[y]&&edge[x][y].cap>edge[x][y].flow){level[y]=level[x]+1;Q.push(y);}}}return level[T]!=0; }int dfs(int x,int cp){//計算可增加流量if(x==n)return cp;int flow=cp;//記錄從x到t的最小殘量for(int y=1;y<=n;y++){if(level[x]+1==level[y]){if(edge[x][y].cap>edge[x][y].flow){int minn=min(flow,edge[x][y].cap-edge[x][y].flow);int newFlow=dfs(y,minn);edge[x][y].flow+=newFlow;edge[y][x].flow-=newFlow;flow-=newFlow;}}if(flow==0)break;}return cp-flow; } int dinic(){int flow=0;int tf=0;while(bfs()){while(tf=dfs(1,INF)){flow+=tf;}}return flow; } int main(){scanf("%d%d",&n,&m);memset(edge,0,sizeof(edge));while(m--){int x,y,w;scanf("%d%d%d",&x,&y,&w);edge[x][y].cap+=w;//便于處理重邊}S=1,T=n;printf("%d\n",dinic());return 0; }2.vector 鄰接表實現
使用 vector 鄰接表與結構體結合,適用于點較多的情況,但速度稍慢于鄰接矩陣。
struct Edge{int from,to;int cap,flow;Edge(){}Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){}}; int n,m; //結點數,邊數(含反向弧) int S,T; //源點、匯點 vector<Edge> edges; //邊表,edges[e]和edges[e^1]互為反向弧 vector<int> G[N]; //鄰接表,G[i][j]表示結點i的第j條邊在e數組中的序號 bool vis[N]; //BFS使用,標記一個節點是否被遍歷過 int dis[N]; //dis[i]表從起點s到i點的距離(層次) int cur[N]; //cur[i]表當前正訪問i節點的第cur[i]條弧 void addEdge(int from,int to,int cap){edges.push_back( Edge(from,to,cap,0) );edges.push_back( Edge(to,from,0,0) );int m=edges.size();G[from].push_back(m-2);G[to].push_back(m-1); } bool BFS(){//構建層次網絡memset(vis,0,sizeof(vis));dis[S]=0;vis[S]=true;queue<int> Q;//用來保存節點編號Q.push(S);while(!Q.empty()){int x=Q.front();Q.pop();for(int y=0;y<G[x].size();y++){Edge& e=edges[G[x][y]];if(!vis[e.to] && e.cap>e.flow){vis[e.to]=true;dis[e.to]=dis[x]+1;Q.push(e.to);}}}return vis[T]; }int DFS(int x,int cp){//cp表示從s到x目前為止所有弧的最小殘量if(x==T || cp==0)return cp;int flow=0,newFlow;//flow用來記錄從x到t的最小殘量for(int &y=cur[x];y<G[x].size();y++){Edge &e=edges[G[x][y]];if(dis[x]+1==dis[e.to]){int minn=min(cp,e.cap-e.flow);newFlow=DFS(e.to,minn);if(newFlow>0){e.flow+=newFlow;edges[G[x][y]^1].flow-=newFlow;flow+=newFlow;cp-=newFlow;if(cp==0)break;}}}return flow; } int Dinic(){int flow=0;while(BFS()){memset(cur,0,sizeof(cur));flow+=DFS(S,INF);}return flow; }int main(){int n,m;scanf("%d%d",&n,&m);//節點編號從1到n,邊編號從0到m-1for(int i=1;i<=n;i++)G[i].clear();edges.clear();for(int i=0;i<m;i++){int u,v,w;scanf("%d%d%d",&u,&v,&w);addEdge(u,v,w);}S=1,T=n;printf("%d\n",Dinic());return 0; }【求最小割】
使用鄰接數組與 STL 中的 set 容器,在求最大流的過程中,將最小割求出。
其原理是最大流最小割定理:網絡的最大流的流量等于最小割的容量
struct Edge{int from,to;int cap,flow;Edge(){}Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){} }; int n,m; //結點數,邊數(含反向弧) int S,T; //源點、匯點 vector<Edge> edges; //邊表,edges[e]和edges[e^1]互為反向弧 vector<int> G[N]; //鄰接表,G[i][j]表示結點i的第j條邊在e數組中的序號 bool vis[N]; //BFS使用,標記一個節點是否被遍歷過 int dis[N]; //dis[i]表從起點s到i點的距離(層次) int cur[N]; //cur[i]表當前正訪問i節點的第cur[i]條弧 set<int> cutSet; //最小割 bool flag; //是否求最小割 void addEdge(int from,int to,int cap){edges.push_back( Edge(from,to,cap,0) );edges.push_back( Edge(to,from,0,0) );int m=edges.size();G[from].push_back(m-2);G[to].push_back(m-1); } bool BFS(){//構建層次網絡memset(vis,0,sizeof(vis));dis[S]=0;vis[S]=true;//將超級源點加入最小割if(flag)cutSet.insert(S);queue<int> Q;//用來保存節點編號Q.push(S);while(!Q.empty()){int x=Q.front();Q.pop();for(int y=0;y<G[x].size();y++){Edge& e=edges[G[x][y]];if(!vis[e.to] && e.cap>e.flow){vis[e.to]=true;dis[e.to]=dis[x]+1;Q.push(e.to);if(flag)//記錄最小割元素cutSet.insert(e.to);}}}return vis[T]; }int DFS(int x,int cp){//cp表示從s到x目前為止所有弧的最小殘量if(x==T || cp==0)return cp;int flow=0,newFlow;//flow用來記錄從x到t的最小殘量for(int &y=cur[x];y<G[x].size();y++){Edge &e=edges[G[x][y]];if(dis[x]+1==dis[e.to]){int minn=min(cp,e.cap-e.flow);newFlow=DFS(e.to,minn);if(newFlow>0){e.flow+=newFlow;edges[G[x][y]^1].flow-=newFlow;flow+=newFlow;cp-=newFlow;if(cp==0)break;}}}return flow; } int Dinic(){int flow=0;while(BFS()){memset(cur,0,sizeof(cur));flow+=DFS(S,INF);}return flow; } int main(){scanf("%d%d",&n,&m);while(m--){int x,y,w;scanf("%d%d%d",&x,&y,&w);addEdge(x,y,w);}S=1,T=n;flag=false;//不統計最小割所屬元素int minCut=Dinic();//計算最小割的值printf("The Min-Cut and Max-Flow:%d\n",minCut);flag=true;//統計最小割所屬元素BFS();//構建層次網絡printf("The Number of Min Cut:%d\n",cutSet.size());//最小割個數printf("The Min Cut Set:");set<int>::iterator it=cutSet.begin();for(;it!=cutSet.end();it++)//最小割元素printf("%d ",*it);printf("\n");return 0; }總結
以上是生活随笔為你收集整理的图论 —— 网络流 —— 最大流 —— Dinic 算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息学奥赛一本通(1021:打印字符)
- 下一篇: 最长配对(51Nod-2494)