上下界的网络流模板
上下界网络流问题对于每一条边、都有流量上下限的限制
而普通的网络流就只有上限限制
下面分别给出几种经典上下界网络流问题的模板
1、无源汇的上下界可行流
实际也就是能否找出一个循环流、使得每个点的流入总流量 == 流出总流量
对于原图的每一条边在网络流中容量应当为 (上界 - 下界)
而后计算每个点流入流量的下界总和记为 in 、流出流量的下界总和记为 out
抽象出超级源汇 ss 与 tt
对于原图中的每一个点
如果 in - out > 0 则 ss 与这个点连边、容量为 in - out
如果 in - out < 0 则 这个点与 tt 连边、容量为 out - in
最后如果 ss 的出边都满流的话、说明可行
给出建图伪代码
for each v in Graph
if( in[v] - out[v] ) connect ss -> v、cap = in[v] - out[v]、FlowSum += in[v] - out[v]
else if( in[v] - out[v] ) connect v -> tt、cap = out[v] - in[v]
最后如果最大流 == FlowSum 则说明可行、否则不行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define LL long long #define ULL unsigned long long #define scl(i) scanf("%lld", &i) #define scll(i, j) scanf("%lld %lld", &i, &j) #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k) #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l) #define scs(i) scanf("%s", i) #define sci(i) scanf("%d", &i) #define scd(i) scanf("%lf", &i) #define scIl(i) scanf("%I64d", &i) #define scii(i, j) scanf("%d %d", &i, &j) #define scdd(i, j) scanf("%lf %lf", &i, &j) #define scIll(i, j) scanf("%I64d %I64d", &i, &j) #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k) #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k) #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k) #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l) #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l) #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l) #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define lowbit(i) (i & (-i)) #define mem(i, j) memset(i, j, sizeof(i)) #define fir first #define sec second #define VI vector<int> #define ins(i) insert(i) #define pb(i) push_back(i) #define pii pair<int, int> #define VL vector<long long> #define mk(i, j) make_pair(i, j) #define all(i) i.begin(), i.end() #define pll pair<long long, long long> #define _TIME 0 #define _INPUT 0 #define _OUTPUT 0 clock_t START, END; void __stTIME(); void __enTIME(); void __IOPUT(); using namespace std; const int maxn = 2000; const int maxm = (10405) << 1; struct Edge{ int x,y,nxt; LL cap; Edge(){} Edge(int a,int b,LL c,LL d) { x=a,y=b,cap=c,nxt=d; } }; struct Dinic{ static const int N=maxn, M=maxm; static const LL INF=0x7fffffff; Edge e[M]; int n,S,T,fst[N],cur[N],EdgeCnt; int q[N],dis[N],head,tail; LL MaxFlow; void Clear(int _n){ n=_n,EdgeCnt=1; memset(fst,0,sizeof fst); } void AddEdge(int a,int b,LL c){ e[++EdgeCnt]=Edge(a,b,c,fst[a]),fst[a]=EdgeCnt; e[++EdgeCnt]=Edge(b,a,0,fst[b]),fst[b]=EdgeCnt; } void init(){ for (int i=1;i<=n;i++) cur[i]=fst[i]; } void init(int _S,int _T){ S=_S,T=_T,MaxFlow=0,init(); } int bfs(){ memset(dis,0,sizeof dis); head=tail=0; q[++tail]=T,dis[T]=1; while (head<tail) for (int x=q[++head],i=fst[x];i;i=e[i].nxt) if (!dis[e[i].y]&&e[i^1].cap){ if (e[i].y==T) return 1; dis[q[++tail]=e[i].y]=dis[x]+1; } return (bool)dis[S]; } LL dfs(int x,LL Flow){ if (x==T||!Flow) return Flow; LL now=Flow; for (int &i=cur[x];i;i=e[i].nxt){ int y=e[i].y; if (dis[x]==dis[y]+1&&e[i].cap){ LL d=dfs(y,min(now,e[i].cap)); e[i].cap-=d,e[i^1].cap+=d,now-=d; if(now==0) break; } } return Flow-now; } LL GetMaxFlow(int _S,int _T){ init(_S,_T); while (bfs()) init(),MaxFlow+=dfs(S,INF); return MaxFlow; } }DC; LL in[maxn], out[maxn], low[maxm]; int main(void){__stTIME();__IOPUT(); int n, m; scii(n, m); DC.Clear(n+2); int ss = n+1, tt = n+2; for(int i=1; i<=m; i++){ int u, v; LL upper; scii(u, v); scll(low[i], upper); out[u] += low[i]; in[v] += low[i]; DC.AddEdge(u, v, upper - low[i]); } LL FlowSum = 0; for(int i=1; i<=n; i++){ if(in[i] - out[i] > 0) DC.AddEdge(ss, i, in[i] - out[i]), FlowSum += in[i] - out[i]; else if(in[i] - out[i] < 0) DC.AddEdge(i, tt, out[i] - in[i]); } if(DC.GetMaxFlow(ss, tt) < FlowSum) puts("NO"); else{ puts("YES"); for(int i=1; i<=m; i++) printf("%lld\n", low[i] + DC.e[i<<1 | 1].cap); } __enTIME();return 0;} void __stTIME() { #if _TIME START = clock(); #endif } void __enTIME() { #if _TIME END = clock(); cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl; #endif } void __IOPUT() { #if _INPUT freopen("in.txt", "r", stdin); #endif #if _OUTPUT freopen("out.txt", "w", stdout); #endif }
2、有源汇的上下界可行流
只要在无源汇的网络流基础上添加多一条边即可
添加从汇到源的一条边、上界为 INF、下界为 0
其余操作和无源汇的操作一样
3、有源汇的上下界最大流
上述可行流算法跑出来的并不一定是最大流
方法就是先跑一遍有源汇的上下界可行流
如果可行流跑出来的结果为可行则
保持这个网络图不要变
再跑一次从源到汇的最大流即为答案
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define LL long long #define ULL unsigned long long #define scl(i) scanf("%lld", &i) #define scll(i, j) scanf("%lld %lld", &i, &j) #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k) #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l) #define scs(i) scanf("%s", i) #define sci(i) scanf("%d", &i) #define scd(i) scanf("%lf", &i) #define scIl(i) scanf("%I64d", &i) #define scii(i, j) scanf("%d %d", &i, &j) #define scdd(i, j) scanf("%lf %lf", &i, &j) #define scIll(i, j) scanf("%I64d %I64d", &i, &j) #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k) #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k) #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k) #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l) #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l) #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l) #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define lowbit(i) (i & (-i)) #define mem(i, j) memset(i, j, sizeof(i)) #define fir first #define sec second #define VI vector<int> #define ins(i) insert(i) #define pb(i) push_back(i) #define pii pair<int, int> #define VL vector<long long> #define mk(i, j) make_pair(i, j) #define all(i) i.begin(), i.end() #define pll pair<long long, long long> #define _TIME 0 #define _INPUT 0 #define _OUTPUT 0 clock_t START, END; void __stTIME(); void __enTIME(); void __IOPUT(); using namespace std; const int maxn = 2000; const int maxm = (10405) << 1; const LL INF=0x7fffffff; struct Edge{ int x,y,nxt; LL cap; Edge(){} Edge(int a,int b,LL c,LL d) { x=a,y=b,cap=c,nxt=d; } }; struct Dinic{ static const int N=maxn, M=maxm; Edge e[M]; int n,S,T,fst[N],cur[N],EdgeCnt; int q[N],dis[N],head,tail; LL MaxFlow; void Clear(int _n){ n=_n,EdgeCnt=1; memset(fst,0,sizeof fst); } void AddEdge(int a,int b,LL c){ e[++EdgeCnt]=Edge(a,b,c,fst[a]),fst[a]=EdgeCnt; e[++EdgeCnt]=Edge(b,a,0,fst[b]),fst[b]=EdgeCnt; } void init(){ for (int i=1;i<=n;i++) cur[i]=fst[i]; } void init(int _S,int _T){ S=_S,T=_T,MaxFlow=0,init(); } int bfs(){ memset(dis,0,sizeof dis); head=tail=0; q[++tail]=T,dis[T]=1; while (head<tail) for (int x=q[++head],i=fst[x];i;i=e[i].nxt) if (!dis[e[i].y]&&e[i^1].cap){ if (e[i].y==T) return 1; dis[q[++tail]=e[i].y]=dis[x]+1; } return (bool)dis[S]; } LL dfs(int x,LL Flow){ if (x==T||!Flow) return Flow; LL now=Flow; for (int &i=cur[x];i;i=e[i].nxt){ int y=e[i].y; if (dis[x]==dis[y]+1&&e[i].cap){ LL d=dfs(y,min(now,e[i].cap)); e[i].cap-=d,e[i^1].cap+=d,now-=d; if(now==0) break; } } return Flow-now; } LL GetMaxFlow(int _S,int _T){ init(_S,_T); while (bfs()) init(),MaxFlow+=dfs(S,INF); return MaxFlow; } }DC; LL in[maxn], out[maxn], low[maxm]; int main(void){__stTIME();__IOPUT(); int n, m, s, t; sciiii(n, m, s, t); DC.Clear(n+2); int ss = n+1, tt = n+2; for(int i=1; i<=m; i++){ int u, v; LL upper; scii(u, v); scll(low[i], upper); out[u] += low[i]; in[v] += low[i]; DC.AddEdge(u, v, upper - low[i]); }DC.AddEdge(t, s, INF); LL FlowSum = 0; for(int i=1; i<=n; i++){ if(in[i] - out[i] > 0) DC.AddEdge(ss, i, in[i] - out[i]), FlowSum += in[i] - out[i]; else if(in[i] - out[i] < 0) DC.AddEdge(i, tt, out[i] - in[i]); } if(DC.GetMaxFlow(ss, tt) < FlowSum) puts("please go home to sleep"); else printf("%lld\n", DC.GetMaxFlow(s, t)); __enTIME();return 0;} void __stTIME() { #if _TIME START = clock(); #endif } void __enTIME() { #if _TIME END = clock(); cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl; #endif } void __IOPUT() { #if _INPUT freopen("in.txt", "r", stdin); #endif #if _OUTPUT freopen("out.txt", "w", stdout); #endif }
4、有源汇的上下界最小流
跑源汇的上下界可行流一次记为 F1
然后添加汇到源的一条边、上界为 INF、下界为 0
添加边后再跑一次上下界可行流一次记为 F2
若 F1 + F2 < ( 超级源点 ss 所有出边的流量之和 )
即满流的情况、则说明可行
最小流就是刚刚添加的从汇到源的那条边的流量
( 不过有点慢、要快的可以上 LOJ 提交记录里面找找快速的代码是如何实现的 )
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define LL long long #define ULL unsigned long long #define scl(i) scanf("%lld", &i) #define scll(i, j) scanf("%lld %lld", &i, &j) #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k) #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l) #define scs(i) scanf("%s", i) #define sci(i) scanf("%d", &i) #define scd(i) scanf("%lf", &i) #define scIl(i) scanf("%I64d", &i) #define scii(i, j) scanf("%d %d", &i, &j) #define scdd(i, j) scanf("%lf %lf", &i, &j) #define scIll(i, j) scanf("%I64d %I64d", &i, &j) #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k) #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k) #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k) #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l) #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l) #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l) #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 #define lowbit(i) (i & (-i)) #define mem(i, j) memset(i, j, sizeof(i)) #define fir first #define sec second #define VI vector<int> #define ins(i) insert(i) #define pb(i) push_back(i) #define pii pair<int, int> #define VL vector<long long> #define mk(i, j) make_pair(i, j) #define all(i) i.begin(), i.end() #define pll pair<long long, long long> #define _TIME 0 #define _INPUT 0 #define _OUTPUT 0 clock_t START, END; void __stTIME(); void __enTIME(); void __IOPUT(); using namespace std; const int maxn = 55000 + 10; const int maxm = (225003 + 1000)<<1; const LL INF=0x7fffffff; struct Edge{ int x,y,nxt; LL cap; Edge(){} Edge(int a,int b,LL c,LL d) { x=a,y=b,cap=c,nxt=d; } }; struct Dinic{ static const int N=maxn, M=maxm; Edge e[M]; int n,S,T,fst[N],cur[N],EdgeCnt; int q[N],dis[N],head,tail; LL MaxFlow; void Clear(int _n){ n=_n,EdgeCnt=1; memset(fst,0,sizeof fst); } void AddEdge(int a,int b,LL c){ e[++EdgeCnt]=Edge(a,b,c,fst[a]),fst[a]=EdgeCnt; e[++EdgeCnt]=Edge(b,a,0,fst[b]),fst[b]=EdgeCnt; } void init(){ for (int i=1;i<=n;i++) cur[i]=fst[i]; } void init(int _S,int _T){ S=_S,T=_T,MaxFlow=0,init(); } int bfs(){ memset(dis,0,sizeof dis); head=tail=0; q[++tail]=T,dis[T]=1; while (head<tail) for (int x=q[++head],i=fst[x];i;i=e[i].nxt) if (!dis[e[i].y]&&e[i^1].cap){ if (e[i].y==T) return 1; dis[q[++tail]=e[i].y]=dis[x]+1; } return (bool)dis[S]; } LL dfs(int x,LL Flow){ if (x==T||!Flow) return Flow; LL now=Flow; for (int &i=cur[x];i;i=e[i].nxt){ int y=e[i].y; if (dis[x]==dis[y]+1&&e[i].cap){ LL d=dfs(y,min(now,e[i].cap)); e[i].cap-=d,e[i^1].cap+=d,now-=d; if(now==0) break; } } return Flow-now; } LL GetMaxFlow(int _S,int _T){ init(_S,_T); while (bfs()) init(),MaxFlow+=dfs(S,INF); return MaxFlow; } }DC; LL in[maxn], out[maxn], low[maxm]; int main(void){__stTIME();__IOPUT(); int n, m, s, t; sciiii(n, m, s, t); DC.Clear(n+2); int ss = n+1, tt = n+2; for(int i=1; i<=m; i++){ int u, v; LL upper; scii(u, v); scll(low[i], upper); out[u] += low[i]; in[v] += low[i]; DC.AddEdge(u, v, upper - low[i]); } LL FlowSum = 0; for(int i=1; i<=n; i++){ if(in[i] - out[i] > 0) DC.AddEdge(ss, i, in[i] - out[i]), FlowSum += in[i] - out[i]; else if(in[i] - out[i] < 0) DC.AddEdge(i, tt, out[i] - in[i]); } LL F1 = DC.GetMaxFlow(ss, tt); int id = (DC.EdgeCnt + 2)>>1; DC.AddEdge(t, s, INF); LL F2 = DC.GetMaxFlow(ss, tt); if(F1+F2 < FlowSum) puts("please go home to sleep"); else printf("%lld\n", DC.e[id << 1 | 1].cap); __enTIME();return 0;} void __stTIME() { #if _TIME START = clock(); #endif } void __enTIME() { #if _TIME END = clock(); cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl; #endif } void __IOPUT() { #if _INPUT freopen("in.txt", "r", stdin); #endif #if _OUTPUT freopen("out.txt", "w", stdout); #endif }
5、有上下界的费用流
不好意思、不会......