定理及模型参见《一 种 简 易 的 方 法 求 解 流 量 有 上 下 界 的 网 络 中 网 络 流 问 题》
详细构图过程及证明参见《Amer图论总结》
1.最简单的是求无源汇点的图的可行流。
若存在可行流,由于每条弧的流量都要大于下限,因此可以将每条弧拆为流量为low的和一条流量为up-low的,称前者为必要弧增加一个附加源x,一个附加汇y,每条有向必要弧(u, v),添加(u, y),(x, v),容量为low。这样就建立了一个等价的网络。附加源x到附加汇y的最大流,能使得x的出弧或者y的入弧都满,充要于原图有可行流。
sgu194 reactor cooling
如果汇源,汇向源连一条没有上下限的边(确保流量平衡条件),于是就转换成了上述问题。
poj2396 budget 代码:点击查看源代码
2.最大流
求完可行流后upflow将附加源和附加汇及关联的弧,残余网络中求解某个流flow,那么upflow+flow仍然是原图的一个可行流,因此对残留网络再求一次最大流就得到了原图的最大流。
zoj3229 shoot the bullet 代码:点击查看源代码
3.最小流
网上看过很多做啊是延残留网络逆向增广求最小流,貌似这样有bug,还是二分枚举原图源点到汇点的流量上届,然后通过验证是否存在可行流比较靠谱,也比较好理解。
sgu176 flow construction 代码:点击查看源代码
4.有上下界的最小费用流
由于上下界只限制流量对费用没太多影响,因此按照以上方法构图应该就可以,但是没有见过这一类题目,也不敢妄下断言。。
与普通最大流相同部分之处就不贴了。。。
int getsap(int s, int t) {
if (s == t)
return inf;
this.s = s;
this.t = t;
Arrays.fill(h, 0);
Arrays.fill(vh, 0);
//flow 不需要在此清零!!!
int ans = 0;
while (h[s] != n)
ans += sap(s, inf);
return ans;
}
void addcap(int a, int b, int low, int up) {
buf[len] = new node(b, low, up, E[a]);
E[a] = len++;
buf[len] = new node(a, 0, 0, E[b]);
E[b] = len++;
sumin[b] += low;
sumout[a] += low;// init()时清零
}
void popcap(int i, int j) {
E[j] = buf[E[j]].ne;
E[i] = buf[E[i]].ne;
len -= 2;
}
int bin(int s,int t,int k){//源点汇点之间上限为k时的最大流
addcap(t, s, 0, k);
for (int i = 0; i < len; i++)
buf[i].flow = 0;
int ans=getsap(n-2,n-1);
popcap(s,t);
return ans;
}
int solve(int s, int t,int mn) {
int total = 0;
int ss = n, tt = n + 1;
for (int i = 0; i < n; i++) {
addcap(i, tt, 0, sumout[i]);
addcap(ss, i, 0, sumin[i]);
total += sumin[i];
}
n += 2;
int ans =bin(s,t,inf);
if (ans != total)
return -1;//不存在可行流
/*求解最小流
int left=0,right=total,mid;
while(left<=right){
mid=(left+right)>>1;
int temp=bin(s,t,mid);
if(temp==total)
{
ans=mid;
right=mid-1;
}
else
left=mid+1;
}
bin(s,t,ans);// ans is the mininum flow
*/
n -= 2;
for (int i = n - 1; i >= 0; i--)
{
popcap(ss, i);
popcap(i, tt);
}
getsap(s,t);
ans=0;
for(int i=0;i<len;i+=2){
buf[i].flow+=buf[i].low;
if(buf[i].be==t)
ans+=buf[i].flow;
}
return ans;
}