362 網路流 最大流 Dinic 演算法
阿新 • • 發佈:2022-05-28
視訊連結:
#include <iostream> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int N=10010,M=200010,INF=1e8; int n,m,S,T; int h[N],e[M],f[M],ne[M],idx; int d[N];//點在分層圖中的編號(即到源點最短路徑) int cur[N];//當前弧優化,儲存從當前節點開始還需要遍歷的邊的編號 void add(int a,int b,int c){ e[++idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx; } bool bfs(){ queue<int> q; memset(d,-1,sizeof d); q.push(S),d[S]=0,cur[S]=h[S]; while(q.size()){ int u=q.front();q.pop(); for(int i=h[u];i;i=ne[i]){ int v=e[i]; if(d[v]==-1&&f[i]){ d[v]=d[u]+1;//點所在層 cur[v]=h[v];//當前弧是第一條邊 if(v==T) return true; q.push(v); } } } return false; } int find(int u,int limit){//多路增廣,累加路流 // 從起點S到當前點u允許流過的最大流量為limit if(u==T) return limit; int flow=0;//從u開始向後面流的最大的流量 // i=cur[u] 跳過前面從u出去的已經流滿的路徑 // flow < limit 說明還有必要搜從u出去的剩餘的邊 for(inti=cur[u];i&&flow<limit;i=ne[i]){ // 如果進入迴圈,說明搜到從u出去編號為i的邊 // 進而說明前面從u指出去的弧已經被搜完了,這些弧的流量已經被"榨乾"了 // 因此dinic下一次呼叫find時,直接從i開始搜即可 cur[u]=i; //當前弧優化 int v=e[i]; if(d[v]==d[u]+1&&f[i]){ // S~u的流量為limit,u~各分支~T的流量和為flow int t=find(v,min(f[i],limit-flow)); // t==0 說明不能找到從v到T的增廣路 // 因此下一次遍歷到v時直接跳過即可 if(!t) d[v]=-1;//枯竭點優化 // 更新殘留網路 f[i]-=t;f[i^1]+=t;flow+=t; } } return flow; } int dinic(){ int res=0,flow; while(bfs()) while(flow=find(S,INF)) res+=flow; return res; } int main(){ int a,b,c; scanf("%d%d%d%d",&n,&m,&S,&T); idx=1; while(m -- ){ scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,0); } printf("%d\n",dinic()); return 0; }