模板:网络流(Dinic算法)
文章目錄
- 1.網絡最大流
- 題目描述
- 解析
- 反悔邊
- 分層(避免環流)
- 時間優化
- 代碼
- 2.費用流
- 描述
- 解析
- 代碼
1.網絡最大流
洛谷P3376
題目描述
給出一個網絡圖,以及其源點和匯點,求出其網絡最大流。
解析
網絡流的思想就是在原有的基礎上不斷進行增廣
基于一個貪心的思路,先bfs判斷是否存在增廣路,再通過dfs增廣
反悔邊
但顯然貪心會出錯(比如當前終點通過其他點來使用會更優時)
所以就引入了反悔邊
就是與所給邊方向相反,一開始容量為0
增廣使用邊時除了把改變的邊容量減去流量,再把反向邊加上同樣的流量即可
這樣以后在必要時,就可以通過走反悔邊的方式撤銷之前的錯誤決策
舉個例子
從S到T
如果貪心的走。先讓2的10給了4
但實際上應該是2給5,3給4最優
那么我們就在2給4時使4到2的邊容量從0加到10
這樣在計算3這個點時就會順著1-3-4-2-5-T走到終點
2-4與4-2流量都為10,等價于把一開始2到4這步操作撤銷了
分層(避免環流)
為了避免dfs環流死循環的情況,我們要把這個圖先分一下層
比如上圖就是:
1層:S
2層:2 3
3層:4 5
4層 T
強制讓dfs只能走到層數+1的點
時間優化
只是這么寫的話會T掉一個點(也可能是我太菜常數太大。。。 )
考慮能否優化
我們發現,每次bfs之后的多次dfs增廣中,這個圖的分層結構是固定的
每個點嘗試走的出邊可能有一些在之前幾次dfs已經用過,再枚舉時其實已經得不到更多的流了
所以我們每次dfs時到每一個點就從上次dfs枚舉到的出邊開始枚舉就行了
但注意,重新bfs后,圖的結構改變,之前沒用的邊可能又能有流了
所以要重新從頭枚舉
代碼
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=300; const int M=15500;struct node{int to,nxt;ll cap; }p[M]; int fi[N],cnt=-1; void addline(int x,int y,ll v){p[++cnt]=(node){y,fi[x],v};fi[x]=cnt; }int n,m,s,t; int a,b,c,d;int dis[N]; int cur[N]; void print(){for(int i=1;i<=n;i++) printf("%d ",dis[i]);printf("\n");} int bfs(){queue<int>q;q.push(s);memset(dis,0,sizeof(dis));dis[s]=1;while (!q.empty()){int x = q.front();q.pop();for (int i = cur[x]= fi[x];~i;i = p[i].nxt){int to = p[i].to;if (dis[to]||!p[i].cap) continue;dis[to] = dis[x] + 1;q.push(to);}}return dis[t]; } ll dfs(int x,ll lim){if(x==t||!lim) return lim; // printf("%d\n",x);ll res=0;for(int &i=cur[x];~i&&lim;i=p[i].nxt){int to=p[i].to;if(dis[to]!=dis[x]+1) continue;ll f=dfs(to,min(p[i].cap,lim));lim-=f;res+=f;p[i].cap-=f;p[i^1].cap+=f;}return res; } ll dinic(){ll ans=0,flow;while(bfs()){while(flow=dfs(s,2e15)) ans+=flow;}return ans; } int main(){memset(fi,-1,sizeof(fi));scanf("%d%d",&m,&n);s=1;t=n;for(int i=1;i<=m;i++){scanf("%d%d%d",&a,&b,&c);addline(a,b,c);addline(b,a,0);}printf("%lld",dinic()); } /* 4 5 4 3 4 2 30 4 3 20 2 3 20 2 1 30 1 3 40 */2.費用流
洛谷P3381
描述
和最大流類似
只是每條邊加了一個單位流的費用
求在最大流的前提下的最小費用方案
解析
上一題搞完這題就好多了
由于要求最小費用,把增廣的bfs改為spfa跑費用的最短路
更新時記錄路徑
尋找路徑上流量最小的值
然后累加費用即可
代碼
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=5500; const int M=55000;struct node{int to,nxt;ll cap,v; }p[M<<1]; int fi[N],cnt=-1; void addline(int x,int y,ll w,ll v){p[++cnt]=(node){y,fi[x],w,v};fi[x]=cnt; }int n,m,s,t; int a,b,c,d;int dis[N]; int pre[N],from[N]; int vis[N]; bool spfa(){pre[t]=0;memset(dis,63,sizeof(dis));memset(vis,0,sizeof(vis));queue<int>q;q.push(s);vis[s]=1;dis[s]=0;while(!q.empty()){int now=q.front();q.pop();vis[now]=0; // printf("now=%d");for(int i=fi[now];~i;i=p[i].nxt){int to=p[i].to;if(p[i].cap==0) continue;if(dis[to]>dis[now]+p[i].v){dis[to]=dis[now]+p[i].v;pre[to]=now;from[to]=i;if(!vis[to]){vis[to]=1;q.push(to);}}}}return pre[t]; } void dinic(){ll flow=0,v=0,tmp;while(spfa()){tmp=2e15;for(int i=t;i!=s;i=pre[i]) tmp=min(tmp,p[from[i]].cap);flow+=tmp;for(int i=t;i!=s;i=pre[i]){v+=tmp*p[from[i]].v;p[from[i]].cap-=tmp;p[from[i]^1].cap+=tmp;}}printf("%lld %lld",flow,v);return; } int main(){memset(fi,-1,sizeof(fi));scanf("%d%d%d%d",&n,&m,&s,&t);for(int i=1;i<=m;i++){scanf("%d%d%d%d",&a,&b,&c,&d);addline(a,b,c,d);addline(b,a,0,-d);}dinic();return 0; } /* 4 5 4 3 4 2 30 4 3 20 2 3 20 2 1 30 1 3 40 */總結
以上是生活随笔為你收集整理的模板:网络流(Dinic算法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建一个收费网站 怎么收费(建一个收费网站
- 下一篇: 货车运输(洛谷P1967)(倍增)