网络流算法

    xiaoxiao2025-09-14  54

    求最大流的过程,就是不断找到一条源到汇的路径,然后构建残余网络,

    再在残余网络上寻找新的路径,使总流量增加,然后形成新的残余网络,

    再寻找新路径…..直到某个残余网络上找不到从源到汇的路径为止,最

    大流就算出来了。

    每次寻找新流量并构造新残余网络的过程,就叫做寻找流量的“增广路径”,也叫“增广”

    各种算法的不同之处,也就是在寻找一条从源到汇的过程中进行优化而已

    1) DFS 

    直接用dfs寻找增广路径,直到找不到路

    这种算法十分简单,但复杂度高

    本来2次就行了,但有可能要200次

    2)Edmonds-Karp 最短增广路算法(BFS)  O(n*m*m)  (n是点数,m是边数)

    利用bfs寻找一条增广路径

    #include<iostream> #include<queue> #include<cstring> #include<cstdio> using namespace std; const int maxn=300; const int INF=(1<<30); int G[maxn][maxn]; int prev[maxn]; //记录前驱节点,记录增广路径 bool vis[maxn]; int Augment(int s,int t,int m) //增广 { queue<int> Q; bool isFound=false; //标记是否找到了增广路 memset(vis,false,sizeof(vis)); memset(prev,0,sizeof(prev)); Q.push(s); vis[s]=true; while(!Q.empty()) { int u=Q.front(); Q.pop(); if(u==t) { //找到增广路径了 isFound=true; break; } for(int v=1;v<=m;v++) //注意是从1开始计数的 if(G[u][v]>0&&!vis[v]){ prev[v]=u; vis[v]=1; Q.push(v); } } if(!isFound) return 0; //无路可走 int MinFlow=INF; int u=m; while(u!=s){ //计算途中最小流 MinFlow=min(MinFlow,G[prev[u]][u]); u=prev[u]; } u=m; while(u!=s){ G[prev[u]][u]-=MinFlow; //更新途径路的流量 G[u][prev[u]]+=MinFlow; //加上反向边 u=prev[u]; } return MinFlow; } int main() { int n,m; while(cin>>n>>m) { int u,v,w; memset(G,0,sizeof(G)); for(int i=0;i<n;i++){ cin>>u>>v>>w; //u-->v 上限是w G[u][v]+=w; //可能有多条边 } int MaxFlow=0; int aug; while(aug=Augment(1,m,m)) //(start,terminal,Node's number) MaxFlow+=aug; //加上每次增广的流 cout<<MaxFlow<<endl; } return 0; }

    3)Dinic 快速网络流算法(BFS+DFS)   O(n*n*m) (n是点数,m是边数)

    因为上述算法都是找到一条从源到汇的路径并进行增广后,重新回到源点继续寻找路径

    但Dinic并不是从头找

    Dinic优点如下:

    1)结合BFS快速的优点,先用BFS将源点到汇点分层,每次DFS时,只能走到下一层结点

    2)因为每次增广后增广路径上至少有一条路径流量上限为0,所以直接返回到增广路径中流量为0的起点进行增广,如果有多条增广路径的流量为0,就选择最上面那一条

    #include<iostream> #include<queue> #include<cstring> #include<cstdio> using namespace std; const int maxn=300; const int INF=(1<<30); int m,n; int G[maxn][maxn]; bool vis[maxn]; int layer[maxn]; //记录层次 bool getLayer(int s,int t,int m) //分层 { queue<int> Q; memset(layer,0,sizeof(layer)); layer[s]=1; Q.push(s); while(!Q.empty()) { int u=Q.front(); Q.pop(); for(int v=1;v<=m;v++) if(G[u][v]>0&&!layer[v]){ layer[v]=layer[u]+1; if(v==t) return true; //一旦到终点m就不必分层了 Q.push(v); } } return false; } int Dinic(int s,int t,int m) { int MaxFlow=0; while(getLayer(s,t,m)) { deque<int> DQ; //保存路径 memset(vis,0,sizeof(vis)); vis[s]=1; DQ.push_back(1); while(!DQ.empty()) { int u=DQ.back(); if(u==t) //找到增广路径了,便找出最小流,更新网络流,添加反向边 { int MinFlow=INF; //最小流 int pMin; //最小流的上一个节点位置 for(int i=1;i<DQ.size();i++) { int u=DQ[i-1]; int v=DQ[i]; if(G[u][v]>0&&MinFlow>G[u][v]) //找出最小流 { MinFlow=G[u][v]; pMin=u; } } MaxFlow+=MinFlow; //累加到最大流 for(int i=1;i<DQ.size();i++) { int u=DQ[i-1]; int v=DQ[i]; G[u][v]-=MinFlow; //更新最小流 G[v][u]+=MinFlow; //添加反向边 } while(!DQ.empty()&&DQ.back()!=pMin){ //退栈到PMin,回溯继续找增广路径 vis[DQ.back()]=0; DQ.pop_back(); } continue; } int ok=0; for(int v=1;v<=m;v++) if(G[u][v]>0&&!vis[v]&&layer[v]==layer[u]+1) //只能走到下一层 { ok=1; vis[v]=1; DQ.push_back(v); break; } if(!ok) DQ.pop_back(); //不能走就pop,换一条路走 } } return MaxFlow; } int main() { while(cin>>n>>m) //从1~m的最大流 //m为顶点数,n为边数 { int u,v,w; memset(G,0,sizeof(G)); for(int i=0;i<n;i++){ cin>>u>>v>>w; //u-->v 上限是w G[u][v]+=w; //可能有多条边 } cout<<Dinic(1,m,m)<<endl; } return 0; }

    转载请注明原文地址: https://ju.6miu.com/read-1302645.html
    最新回复(0)