上下界网络流

本篇笔记写于远古时代,写时虽然参考了一些的资料(罗列在文末),但口胡仍然占多数,准确性堪忧,如有不准确的地方,欢迎指正。

无源汇有上下界可行流

首先要满足流量下界的需求,强行让每条边都流过low的流量,那么每条边只剩下的容量为upp-low,同时设流入点x的流量-流出点x的流量为d[x]。

考虑调整流网络使得流量平衡(让d[x]变为0)1)对于d[x]<0的点,我们希望向x流入-d[x]的流量 2)对于d[x]>0的点,我们希望从x流出d[x]的流量;

因此,考虑新建附源汇点S-T,S指向所有d[x]>0的x,所有d[x]>0的x指向T就能提供像这样调整的“势”。

对流网络跑最大流,如果存在边(S,?,k)或(?,T,k)满足k!=0,则可知无法调整到流量平衡,即不存在可行流。 因为sum a[x] (a[x]>0)=sum -a[x] (a[x]<0),具体操作时,只检差一边就行了。

显然,此时的一条边的可行流量为跑完最大流后所减少的流量(即反边流量)+原来的下界。

#include <bits/stdc++.h>
using namespace std;
const int N=207; 
const int L=30407;
const int inf=0x3f3f3f3f;

int S,T;
int head[N],to[L],cap[L],last[L],cnt=1;
int que[N],lev[N],hd,tl;

inline void add_edge(int x,int y,int c1,int c2=0) {
    to[++cnt]=y,cap[cnt]=c1,last[cnt]=head[x],head[x]=cnt;
    to[++cnt]=x,cap[cnt]=c2,last[cnt]=head[y],head[y]=cnt; 
}
inline bool bfs() {
    memset(lev,0,sizeof lev);
    lev[S]=1;
    que[hd=0,tl=1]=S;
    while(hd<tl) {
        int x=que[++hd];
        for(int i=head[x]; i; i=last[i]) if(cap[i]>0 && !lev[to[i]]) 
            lev[to[i]]=lev[x]+1, que[++tl]=to[i];
    }
    return lev[T]!=0;
}
int dfs(int x,int tf) {
    if(x==T) return tf;
    int tot=0,tmp;
    for(int i=head[x]; i; i=last[i]) if(cap[i]>0 && lev[x]+1==lev[to[i]]) {
        tmp=dfs(to[i],min(tf-tot,cap[i]));
        if(tmp) cap[i]-=tmp,cap[i^1]+=tmp,tot+=tmp;
        if(tot==tf) break;
    }
    if(!tot) lev[x]=-1;
    return tot;
}
int n,m,a[N],low[L]; 
bool check() {
    for(int i=head[S]; i; i=last[i]) 
        if(cap[i]) return false;
    for(int i=head[T]; i; i=last[i])
        if(cap[i^1]) return false;
    return true;
}
void solve() {
    cnt=1;
    memset(a,0,sizeof a);
    memset(head,0,sizeof head);
    for(int i=1,x,y,upp; i<=m; ++i) {
        scanf("%d%d%d%d",&x,&y,&low[i],&upp);
        a[x]-=low[i];
        a[y]+=low[i];
        add_edge(x,y,upp-low[i]);
    }
    int lst=cnt;
    S=n+1, T=n+2;
    for(int i=1; i<=n; ++i) {
        if(a[i]>0) add_edge(S,i,a[i]);
        if(a[i]<0) add_edge(i,T,-a[i]);
    }
    while(bfs()) dfs(S,inf);
    if(!check()) {
        puts("NO");
        return;
    }
    puts("YES"); 
    for(int i=2; i<=lst; i+=2) {
        printf("%d\n",cap[i^1]+low[i>>1]);
    }
}

int main() {
    while(~scanf("%d%d",&n,&m)) solve();
    return 0;
}

有源汇有上下界可行流

无源汇的流网络所有点都要满足d[x]=0,这说明它其实是一个“流循环图”。而对于有源汇的合法的流网络,除了d[s]<0,d[t]>0外,其余点满足d[x]=0,并且d[s]=-d[t],这启发我们加入t到s的一条下界为0,上界为inf的边,这样,图转化为一个“流循环图”,可以借助无源汇的算法调整。

来看下调整后流网络的边t-s,它的反边流量k=与s、t有关的基准流量,即这样的一组调整必然会给s-t的最大流带来k的贡献。答案即为k。

#include <bits/stdc++.h>
using namespace std;

const int N=207; 
const int L=30407;
const int inf=0x3f3f3f3f;

int S,T,s,t;
int head[N],to[L],cap[L],last[L],cnt=1;
int que[N],lev[N],hd,tl;

inline void add_edge(int x,int y,int c1,int c2=0) {
    to[++cnt]=y,cap[cnt]=c1,last[cnt]=head[x],head[x]=cnt;
    to[++cnt]=x,cap[cnt]=c2,last[cnt]=head[y],head[y]=cnt; 
}
inline bool bfs() {
    memset(lev,0,sizeof lev);
    lev[S]=1;
    que[hd=0,tl=1]=S;
    while(hd<tl) {
        int x=que[++hd];
        for(int i=head[x]; i; i=last[i]) if(cap[i]>0 && !lev[to[i]]) 
            lev[to[i]]=lev[x]+1, que[++tl]=to[i];
    }
    return lev[T]!=0;
}
int dfs(int x,int tf) {
    if(x==T) return tf;
    int tot=0,tmp;
    for(int i=head[x]; i; i=last[i]) if(cap[i]>0 && lev[x]+1==lev[to[i]]) {
        tmp=dfs(to[i],min(tf-tot,cap[i]));
        if(tmp) cap[i]-=tmp,cap[i^1]+=tmp,tot+=tmp;
        if(tot==tf) break;
    }
    if(!tot) lev[x]=-1;
    return tot;
}
int n,m,a[N],low[L]; 
bool check() {
    for(int i=head[S]; i; i=last[i]) 
        if(cap[i]) return false;
    for(int i=head[T]; i; i=last[i])
        if(cap[i^1]) return false;
    return true;
}
void solve() {
    cnt=1;
    memset(a,0,sizeof a);
    memset(head,0,sizeof head);
    for(int i=1,x,y,upp; i<=m; ++i) {
        scanf("%d%d%d%d",&x,&y,&low[i],&upp);
        a[x]-=low[i];
        a[y]+=low[i];
        add_edge(x,y,upp-low[i]);
    }
    add_edge(t,s,inf);
    S=n+1, T=n+2;
    for(int i=1; i<=n; ++i) {
        if(a[i]>0) add_edge(S,i,a[i]);
        if(a[i]<0) add_edge(i,T,-a[i]);
    }
    while(bfs()) dfs(S,inf);
    if(!check()) {
        puts("please go home to sleep");
        return;
    }
    printf("%d\n",upp[head[s]]);
}

int main() {
    while(~scanf("%d%d%d%d",&n,&m,&s,&t)) 
        solve();
    return 0;
}

有源汇有上下界最大流

接着第二来讲,因此,再将此边即所有与ST关联的边删除,剩下的便是张一般的流网络,k+此时s-t的最大流即为答案。

进一步的,我们可以不必删除t-s边和ST关联的边,直接采用当前流网络的s-t的最大流作为答案。因为1)t-s边的反边的基准流量必然会被增广;2)不可能存在经过S或T的增广路(S的出边、T的入边全部断开)。

#include <bits/stdc++.h>
using namespace std;

const int N=207; 
const int L=30407;
const int inf=0x3f3f3f3f;

int S,T,s,t;
int head[N],to[L],cap[L],last[L],cnt=1;
int que[N],lev[N],hd,tl;

inline void add_edge(int x,int y,int c1,int c2=0) {
    to[++cnt]=y,cap[cnt]=c1,last[cnt]=head[x],head[x]=cnt;
    to[++cnt]=x,cap[cnt]=c2,last[cnt]=head[y],head[y]=cnt; 
}
inline bool bfs() {
    memset(lev,0,sizeof lev);
    lev[S]=1;
    que[hd=0,tl=1]=S;
    while(hd<tl) {
        int x=que[++hd];
        for(int i=head[x]; i; i=last[i]) if(cap[i]>0 && !lev[to[i]]) 
            lev[to[i]]=lev[x]+1, que[++tl]=to[i];
    }
    return lev[T]!=0;
}
int dfs(int x,int tf) {
    if(x==T) return tf;
    int tot=0,tmp;
    for(int i=head[x]; i; i=last[i]) if(cap[i]>0 && lev[x]+1==lev[to[i]]) {
        tmp=dfs(to[i],min(tf-tot,cap[i]));
        if(tmp) cap[i]-=tmp,cap[i^1]+=tmp,tot+=tmp;
        if(tot==tf) break;
    }
    if(!tot) lev[x]=-1;
    return tot;
}
int n,m,a[N],low[L]; 
bool check() {
    for(int i=head[S]; i; i=last[i]) 
        if(cap[i]) return false;
    for(int i=head[T]; i; i=last[i])
        if(cap[i^1]) return false;
    return true;
}
void solve() {
    cnt=1;
    memset(a,0,sizeof a);
    memset(head,0,sizeof head);
    for(int i=1,x,y,upp; i<=m; ++i) {
        scanf("%d%d%d%d",&x,&y,&low[i],&upp);
        a[x]-=low[i];
        a[y]+=low[i];
        add_edge(x,y,upp-low[i]);
    }
    add_edge(t,s,inf);
    S=n+1, T=n+2;
    for(int i=1; i<=n; ++i) {
        if(a[i]>0) add_edge(S,i,a[i]);
        if(a[i]<0) add_edge(i,T,-a[i]);
    }
    while(bfs()) dfs(S,inf);
    if(!check()) {
        puts("please go home to sleep");
        return;
    }
    int ret=0;
    S=s, T=t;
    while(bfs()) ret+=dfs(S,inf);
    printf("%d\n",ret);
}

int main() {
    while(~scanf("%d%d%d%d",&n,&m,&s,&t)) 
        solve();
    return 0;
}

有源汇有上下界最小流

直接的做法是s-t的最小流=基准流量k - (t-s的最大流)。此时要注意删除t-s的边(若不删除,则会使得t-s的最大流接近无穷大,成功GG)。

#include <bits/stdc++.h>
#define LL long long 
using namespace std;

const int N=5e4+20;
const int L=2e6+20;
const LL inf=1e15;

int n,m,s,t,d[N];
int S=N-1,T=N-2;
int head[N],cur[N],to[L],last[L],cnt=1;
int que[N],lev[N],hd,tl;
LL upp[L];

inline void add_edge(int x,int y,LL u) {
    to[++cnt]=y,upp[cnt]=u,last[cnt]=head[x],head[x]=cnt;
    to[++cnt]=x,upp[cnt]=0,last[cnt]=head[y],head[y]=cnt; 
}
inline bool bfs() {
    memset(lev,0,(n+2)<<2);
    lev[S]=1;
    que[hd=0,tl=1]=S;
    while(hd<tl) {
        int x=que[++hd];
        for(int i=head[x]; i; i=last[i]) if(upp[i]>0 && !lev[to[i]]) 
            lev[to[i]]=lev[x]+1, que[++tl]=to[i];
    }
    return lev[T]!=0;
}
LL dfs(int x,LL tf) {
    if(x==T) return tf;
    LL tmp;
    for(int &i=cur[x]; i; i=last[i]) if(upp[i]>0 && lev[x]+1==lev[to[i]]) {
        tmp=dfs(to[i],min(tf,upp[i]));
        if(tmp) {
            upp[i]-=tmp;
            upp[i^1]+=tmp;
            return tmp;
        }
    }
    lev[x]=-1;
    return 0;
}
template<typename T> inline void read(T&d) {
    register char ch=getchar(); d=0;
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) d=d*10+ch-'0',ch=getchar();
} 
LL dinic() {
    LL ret=0,tmp;
    while(bfs()) {
        for(int i=0; i<=n+1; ++i) cur[i]=head[i];
        while((tmp=dfs(S,inf))>0) ret+=tmp;
    }
    return ret;
}
int main() {
    read(n);read(m);read(s);read(t);
    for(int i=1,x,y,low,upp; i<=m; ++i) {
        read(x);read(y);read(low);read(upp);
        d[x]-=low;
        d[y]+=low;
        add_edge(x,y,upp-low); 
    }
    S=0, T=n+1;
    add_edge(t,s,inf);
    for(int i=1; i<=n; ++i) {
        if(d[i]>0) add_edge(S,i,d[i]);
        if(d[i]<0) add_edge(i,T,-d[i]);
    }
    dinic();
    for(int i=head[S]; i; i=last[i]) {
        if(upp[i]) {
            puts("please go home to sleep");
            return 0;
        }
    }
    long long st=upp[head[s]];
    upp[head[s]]=0;
    upp[head[t]]=0;
    S=t, T=s;
    printf("%lld\n",st-dinic());
    return 0;
}

无源汇有上下界最小费用可行流

干掉下界的时候,把费用先计算上。把超级源超级汇求最大流的一步,换成最小费用最大流即可。即满足合法的前提下,最小化费用。费用就是之前的费用加上这次的费用。

有源汇有上下界最小费用可行流

参考第一到第二的转化,添加边t-s,下界为0,上界为inf,费用为0。然后按照第五进行调整至所有点流量平衡即可。

有源汇有上下界最小费用最大流

似乎直接按照第三改一波。。mf改为mcmf。

有源汇有上下界最小费用最小流

似乎直接按照第四改一波。。我也不清楚,因为要减去最多的花费 最大费用流???

参考资料:

学习笔记 上下界网络流 作者*Miracle*。

网络流常见建模总结 作者panda_2134。

转载于:https://www.cnblogs.com/nosta/p/10938396.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值