先摆上网络流的经典问题 :给你一个图,每条边表示能过通过这条变的最大流量,问你从源点最多流多少到终点
然后介绍增广路算法:每次选一条从起点到终点的路径,并且答案ans加上这条路径的最小值,并且反向建边,权值为流过的花费,至于反向建边的意义看这个图吧
EK算法:用bfs跑最短路,然后dfs找最小值
dinic优化: 首先了解下分层图的概念 从起点到终点路径相同的点如{1,2,4},{1,3,4} 。优化时用dfs遍历所有相同层的图直到找不到路径了就退出去找找下一层的图
时间复杂度分析(百度词条):因为在Dinic的执行过程中,每次重新分层,汇点所在的层次是严格递增的,而n个点的层次图最多有n层,所以最多重新分层n次。在同一个层次图中,因为每条增广路都有一个瓶颈,而两次增广的瓶颈不可能相同,所以增广路最多m条。搜索每一条增广路时,前进和回溯都最多n次,所以这两者造成的时间复杂度是O(nm);而沿着同一条边(i,j)不可能枚举两次,因为第一次枚举时要么这条边的容量已经用尽,要么点j到汇不存在通路从而可将其从这一层次图中删除。综上所述,Dinic算法时间复杂度的理论上界是O(n^2*m)。
#include<bits/stdc++.h> using namespace std; const int MAX=1e6+5; const int INF=0x3f3f3f3f; struct Node{ int to,ne,w; }e[MAX]; int head[MAX],dis[MAX]; int n,m,s,t,cnt; queue<int>que; void init(){ while(!que.empty()) que.pop(); cnt=0; memset(head,-1,sizeof head); } void add(int u,int v,int w){//链式前向星建图 e[cnt].ne=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt++; } int bfs(){ memset(dis,0,sizeof dis); que.push(s); dis[s]=1; while(!que.empty()){ int v=que.front(); que.pop(); for(int i=head[v];~i;i=e[i].ne){ if(e[i].w>0&&dis[e[i].to]==0){ que.push(e[i].to); dis[e[i].to]=dis[v]+1; } } } if(dis[t]==0) return 0;//是否联通 else return 1; } int dfs(int u,int val){ if(u==t){ return val; } for(int i=head[u];~i;i=e[i].ne){ if(dis[e[i].to]==dis[u]+1&&e[i].w>0){ int va=dfs(e[i].to,min(val,e[i].w)); if(va!=0){ e[i].w-=va; e[i^1].w+=va; return va; } } } return 0; } int dinic(){ int ans=0; while(bfs()){ int cc; while(cc=dfs(s,INF))ans+=cc;//dinic优化,遍历所有bfs跑出来的全部最短路 } return ans; } int main(){ // freopen("1.txt","r",stdin); cin>>n>>m>>s>>t; init(); for(int i=0;i<m;i++){ int u,v,val; cin>>u>>v>>val; add(u,v,val); add(v,u,0); } cout<<dinic()<<endl; }