標籤:c++
問題模型:
給定一個加權的有向圖,滿足:
(1)容量限制條件:
(2)流量平衡條件:
(2)中的即除了源匯外,所有點都滿足流量平衡條件,則稱G為有源匯網路;否則,即不存在源匯,所有點都滿足流量平衡條件,則稱G為無源匯網路。
將這類問題由易到難一一解決:
問題[1] 求無源匯的網路有上下界的可行流
由於下界是一條弧上的流必需要滿足的確定值。下面引入必要弧的概念:必要弧是一定流要滿的弧。必要弧的構造,將容量下界的限制分離開了,從而構造了一個沒有下界的網路G’:
1. 將原弧(u,v)分離出一條必要弧:。(紅色表示)
2. 原弧:。
由於必要弧的有一定要滿的限制,將必要弧“拉”出來集中考慮:
添加附加源x, 附加匯y。想像一條不限上界的(y, x),用必要弧將它們“串”起來,即對於有向必要弧(u, v),添加(u, y),(x, v),容量為必要弧容量。這樣就建立了一個等價的網路。
一個無源匯網路的可行流的方案一定是必要弧是滿的。若去掉(y, x)後,附加源x到附加匯y的最大流,能使得x的出弧或者y的入弧都滿,充要於原圖有可行流。
問題[2] 求無源匯的網路有上下界的可行流
演算法:
1. 按上述方法構造新網路(分離必要弧,附加源匯)
2. 求附加源x到附加匯y的最大流
3. 若x的出弧或y的入弧都滿,則有解,將必要弧合并回原圖;否則,無解。
zoj2314(無源匯點 有上下界 可行流的判斷)
代碼:
#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#include <queue>const int MAXN =505;const int MAXM=440020;const int INF=0x3f3f3f3f;using namespace std;struct Edge { int to,cap,flow,next;} edge[MAXM];int head[MAXN],tot,gap[MAXN],d[MAXN],cur[MAXN],que[MAXN],p[MAXN];int n;int id[MAXM];void init(){ tot=0; memset(head,-1,sizeof(head));}void addedge(int u,int v,int c,int d){ edge[tot]=(Edge){v,c,0,head[u]}; id[d]= head[u] = tot++; edge[tot]=(Edge){u,c,c,head[v]}; head[v] = tot++;}int isap(int source,int sink,int N){ memset(gap,0,sizeof(gap)); memset(d,0,sizeof(d)); memcpy(cur,head,sizeof(head)); int top = 0,x = source,flow = 0; while(d[source] < N) { if(x == sink) { int Min = INF,inser=0; for(int i = 0; i < top; ++i) { if(Min > edge[p[i]].cap - edge[p[i]].flow) { Min = edge[p[i]].cap - edge[p[i]].flow; inser = i; } } for(int i = 0; i < top; ++i) { edge[p[i]].flow += Min; edge[p[i]^1].flow -= Min; } if(Min!=INF) flow += Min; top = inser; x = edge[p[top]^1].to; continue; } int ok = 0; for(int i = cur[x]; i != -1; i = edge[i].next) { int v = edge[i].to; if(edge[i].cap > edge[i].flow && d[v]+1 == d[x]) { ok = 1; cur[x] = i; p[top++] = i; x = edge[i].to; break; } } if(!ok) { int Min = N; for(int i = head[x]; i != -1; i = edge[i].next) { if(edge[i].cap > edge[i].flow && d[edge[i].to] < Min) { Min = d[edge[i].to]; cur[x] = i; } } if(--gap[d[x]] == 0) break; gap[d[x] = Min+1]++; if(x != source) x = edge[p[--top]^1].to; } } return flow;}int main(){// freopen("in.txt","r",stdin); int T,m,ss; int a,b,low,up; int d[MAXN]; int l[MAXM]; scanf("%d",&T); while(T--) { init(); memset(d,0,sizeof(d)); ss=0; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&a,&b,&low,&up); addedge(a,b,up-low,i); d[a]+=low; d[b]-=low; l[i]=low; } for(int i=1;i<=n;i++) { if(d[i]>0){ addedge(i,n+1,d[i],0); ss+=d[i]; } else addedge(0,i,-d[i],0); } if(isap(0,n+1,n+2)==ss){ printf("YES\n"); for(int i=1;i<=m;i++) //最後輸出可行流 printf("%d\n",edge[id[i]].flow+l[i]); } else printf("NO\n"); } return 0;}
問題[2] 求有源匯的網路有上下界的可行流
加入邊(t, s),下界為0(保證不會連上附加源匯x, y),不限上界,將問題[2]轉化為問題[1]來求解。
問題[3]求有源匯的網路有上下界的最大流
演算法:
1. 先轉化為問題[2]來求解一個可行流。若可行無解,則退出。由於必要弧是分離出來的,所以就可以把必要弧(附加源匯及其臨邊)及其上的流,暫時刪去。再將(T,S)刪去,恢複源匯。
2. 再次,從S到T找增廣軌,求最大流。
3. 最後將暫時刪去的下界資訊恢複,合并到當前圖中。輸出解。
這樣既不破壞下界(分離出來)也不超出上界(第2步滿足容量限制),問題解決。
問題[4]求有源匯的網路有上下界的最小流
演算法:
1. 同問題[3]。
2. 從T到S找增廣軌,不斷反著改進。
3. 同問題[3]。
問題[3]與問題[4]的另一種簡易求法:
注意問題[2]中,構造出的(t, s),上下界幾乎沒什麼限制。下面看看它的性質:
定理:如果從s到t有一個流量為a的可行流f,那麼從t到s連一條弧(t, s),其流量下界b(t, s) = a,則這個圖一定有一個無源匯的可行流:除了弧(t, s)的容量為a外,其餘邊的容量與f相同。
證明:如果從s到t的最大流量為amax,那麼從t到s連一條下界b(t, s) = a’ > amax的弧(t, s),則從在這個改造後的圖中一定沒有無源匯的可行流:否則將這個可行流中的弧(t, s)除去,就得到了原圖中s到t的流量為a’的流,大於最大流量amax,產生矛盾。
可以二分枚舉這個參數a,即下界b(t, s),每次用問題[1]判斷是否有可行流。這樣就可以求出最大流。
同理,問題[4]要求最小流,只要二分枚舉上界c(t, s)即可。
因為樸素的預流推進演算法O(N3),總複雜度為O(N3 log2流量) 。
思路:
無源匯 (附加源匯+最大解決)
有源匯 (附加(T,S)->無源匯)
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
有上下界的最大流解法