前幾天就想寫了的,一直沒寫,今天就寫完吧。
因為在做這些上下界的題的時候,遇到了很多問題,在大神的幫助下還是一一解決了的。(英文沒學好誒喂,,在sgu和poj各種wa。。)
主要是沒看題,求上下界已經理解了的。。
做法:將圖的下界分離到乙個附加源和匯中,而上界則變為原弧的上界減去下界的差,構成乙個附加網路,再在附加源匯上跑一次最大流即可。其中,記錄每個點的入流下界和-出流下界和,當下界和》0時怎連入一條源s到這個點的邊,上界為這個下界和,下界為0;當下界和<0則連入一條這個點到匯t的邊,上界為下界和的絕對值,下界為0。
判斷:僅當以源s為起點的弧或以匯t為終點的弧全部流滿,則滿足下界的可行流才存在,否則不存在。
例題:sgu194
**:
#include #include using namespace std;
#define cc(a, c) memset(a, c, sizeof(a))
#define for(i,a,n) for(i=a;i<=n;++i)
const int maxn=500, oo=~0u>>1, maxm=80810;
int s, t, cap[maxm], d[maxn], cur[maxn], gap[maxn], p[maxn];
int n, ihead[maxn], inext[maxm], to[maxm], from[maxm], cnt;
int dn[maxm], du[maxn];
int min(const int& a, const int& b)
int isap()
} return flow;
}void add(int u, int v, int c)
void init(int s, int t, int n)
int main()
for(i=1; i<=n; ++i)
isap();
flag=1;
for(i=ihead[s]; i; i=inext[i]) if(cap[i]) //僅當流滿才存在
if(!flag) puts("no");
else
} return 0;
}
做法:從匯t連一條弧到源s,上界為無限大,下界為0。再設乙個超級源ss和乙個超級匯tt,按照無源匯上下界最大流的方法連邊給ss和tt,從ss跑最大流到tt,判斷是否有弧滿後,去掉超級源和匯及其邊,跑一次s-t的最大流(注意這裡最大流絕不是t-s的反向弧,因為在第二次跑最大流的時候將這弧退掉了),最後根據題目要求輸出即可。
技巧:我用的是鏈式前向星(鄰接表),因此將ss和tt的head去掉即可,為什麼呢,因為這不會影響到後面跑最大流,因為ss和tt本來就不在s-t的增廣路上,但這裡要注意,因為ss和tt還在圖中,因此呼叫最大流的演算法時,點的數量要包括了ss和tt。
例題:zoj3229
ps:這題是我除錯了2天的。。太坑了,很多細節,還是注意看題吧。。比如說按輸入的順序輸出。。導致我演算法沒錯則一直在找演算法的錯誤,還是請大神幫我找出來的。。。。。但是還是找到了一些bug且大神幫我優化了我的sap的乙個地方,還是很感謝他的~
ps:這題樣例是有問題的,請不要相信第乙個case,用第二個case來做測試吧
**:
#include #include using namespace std;
#define cc(a, c) memset(a, c, sizeof(a))
#define for(i,a,n) for(i=a;i<=n;++i)
const int maxn=1510, oo=100000000, maxm=151000;
int cap[maxm], d[maxn], cur[maxn], gap[maxn], p[maxn];
int ihead[maxn], inext[maxm], to[maxm], from[maxm], cnt;
int inout[maxn], down[400][1200], id[400][1200]; //這裡用maxn*maxn的話就mle了。。
int e[maxm][2], e;
int min(const int& a, const int& b)
int isap(int s, int t, int n)
} return flow;
}void add(int u, int v, int c)
void init()
int main()
for(i, 1, n)
} add(t, s, oo); //新增匯到源的oo弧
s=n+m+3; t=n+m+4; //設定超級源和匯
sum=0;
for(i, 1, n+m+2) //sum是判斷滿流的一種方法
if(inout[i]<0) add(i, t, -inout[i]);
} if(isap(s, t, t)!=sum) printf("-1\n");
else
printf("\n");
} return 0;
}
做法:先不連邊t-s,先構造了附加網路到超級源和超級匯中,然後跑超級源到超級匯的最大流,再連匯t到源s上界為無限大下界為0的弧,再跑一次超級源到超級匯的最大流即可,最小流就是t-s的反向弧。。。(ps,這裡我不明白為什麼這樣做,問了之前幫我的大神,他也不知道,因此我先放下吧,以後問問其它大神。。其實我有乙個理解,就是先將之前 上界-下界 的那些弧的最大跑出來, 然後連邊後再跑,其實就是退流,將之前跑的退回去,這樣既滿足了下界限制,又是可行流,因為退流回去的是最大流,因此第二次跑出來的是最小流)
例題:sgu176
**:
#include #include using namespace std;
#define cc(a, c) memset(a, c, sizeof(a))
#define for(i,a,n) for(i=a;i<=n;++i)
const int maxn=1510, oo=100000000, maxm=151000;
int cap[maxm], d[maxn], cur[maxn], gap[maxn], p[maxn];
int ihead[maxn], inext[maxm], to[maxm], from[maxm], cnt;
int inout[maxn], ans[maxm], id[maxm];
int min(const int& a, const int& b)
int isap(int s, int t, int n)
} return flow;
}void add(int u, int v, int c, int _id)
void init()
int main()
e=cnt;
ss=n+1; tt=ss+1;
for(i, 1, n)
isap(ss, tt, tt); //先跑一次最大流
add(t, s, oo, 0); //連邊
isap(ss, tt, tt); //再跑一次最大流
for(i=ihead[ss]; i; i=inext[i]) if(cap[i]) break;
if(i) puts("impossible");
else
} return 0;
}
周源《一種簡易的方法求解流量有上下界的網路中網路流問題》
mr. ant部落格:
有上下界網路流問題
此類問題可以分為三小類問題 一 無源匯有上下界最大流 二 有源匯有上下界最大流 三 有源匯有上下界最小流 1 無源匯有上下界最大流 題目大意 給n個點,及m根pipe,每根pipe用來流躺液體的,單向的,每時每刻每根pipe流進來的物質要等於流出去的物質,要使得m條pipe組成乙個迴圈體,裡面流躺物...
有上下界網路流
前言 下面寫得只是一些十分基礎的東西,是給我以後自己看的,想要徹底弄明白這個內容,推薦去看liu runda。注 為了方便,下面所有的 x,y,l,r 都表示一條從x連向y,流量下界為l,流量上界為r的邊。問題簡述 給出乙個有向圖,每條邊有流量上下界,沒有源點和匯點,要求找到一種流的方法,使得每個點...
有上下界的網路流
有上下界的網路流 這幾天看了周源的 一種簡易的方法求解流量有上下界的網路中網路流問題 並完成了 sgu 194 zoj 2314 reactor cooling,sgu 176 flow construction 和hoj 2135 poj 2396 budget三道題。作為周源文章中提到的求解上下...