日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

模板:网络流(Dinic算法)

發布時間:2023/12/3 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 模板:网络流(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算法)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。