有源汇有上下界最小流详解(loj117)

来源:互联网 发布:魔法王座神器7上8数据 编辑:程序博客网 时间:2024/06/05 03:02

例题链接
首先还是得吐槽一下题意。
题意是这样的:
求出一个流使得源点的总流出量等于汇点的总流入量,其他的点满足流量守恒,而且每条边的流量满足上界和下界限制。在这些前提下要求总流量最小。

解法:
没学过无源汇有上下界可行流的还是先学学吧

好像是有两种做法的。
做法1:
首先先求出可行流,按照上一篇博客的做法即可。
上一篇博客
很明显现在已经满足下界了,那么我们只需在残量网络上求出最小流即可。
此时需要理解反向边。
反向边的流量增加等价于正向边的的流量减少。
那么t到s的流就相当于s到t减少的流。
那么求出t到s的最大流就相当于s到t减少最多的流,那么剩下的就是最小流。
所以最后的答案就是可行流大小-t到s的最大流

然而我并没有用第一种做法(以后可能会补代码吧)

做法2:(其实我觉得做法1更容易理解)
建立超级源和超级汇之后呢。
先跑一次最大流。
然后t到s连一条无穷大的边。
再跑一次最大流。
最后的答案就是无穷大的边流过的流量。
有点难理解。
首先因为整个图满足流量平衡。
所以说t点流入的流量等于流出的流量。
那么他流出的流量只有那一条无穷大的边而已。
所以说最小流就是求那条边(无穷边)流过的流量尽量小而已。

所以说第一次流最大流的时候已经满足下界。那么满足下界能流的边已经流满。
所以残量网络剩下的最大流就会尽可能的小了。

大概就是这样吧。
代码实现:

#include<cstdio>#include<cstring>#include<cstdlib>#include<algorithm>#include<iostream>#include<cmath>using namespace std;typedef long long ll;const ll inf=1e10;int st,ed;struct node {    int x,y,other,next;    ll c;}a[1100000];int last[71000],len;int h[71000],list[71000];ll d[71000];void ins(int x,int y,ll c) {    int k1,k2;    len++;k1=len;    a[len].x=x;a[len].y=y;a[len].c=c;    a[len].next=last[x];last[x]=len;    len++;k2=len;    a[len].x=y;a[len].y=x;a[len].c=0;    a[len].next=last[y];last[y]=len;    a[k1].other=k2;    a[k2].other=k1;}int head,tail;bool bfs() {        memset(h,0,sizeof(h));h[st]=1;    head=1;tail=2;list[1]=st;    while(head!=tail) {        int x=list[head];        for(int k=last[x];k;k=a[k].next) {            int y=a[k].y;            if(a[k].c>0&&h[y]==0) {                h[y]=h[x]+1;                    list[tail++]=y;                if(tail==ed+1)                    tail=1;            }        }        head++;        if(head==ed+1)            head=1;    }    if(h[ed]==0)        return false;    return true;}ll findflow(int x,ll f){    if(x==ed)         return f;    ll s=0,t;    for(int k=last[x];k;k=a[k].next)    {        int y=a[k].y;        if(h[y]==h[x]+1&&a[k].c>0&&s<f) {            t=findflow(y,min(a[k].c,f-s));            a[k].c-=t;a[a[k].other].c+=t;            s+=t;        }    }    if(s==0)        h[x]=0;    return s;}ll Dinic(){    ll ans=0;    while(bfs()==true)        ans+=findflow(st,inf);    return ans;}int main(){    int n,m,s,t;    scanf("%d%d%d%d",&n,&m,&s,&t);    len=0;memset(last,0,sizeof(last));    for(int i=1;i<=m;i++) {        int x,y;ll shang,xia;        scanf("%d%d%lld%lld",&x,&y,&xia,&shang);        ins(x,y,shang-xia);        d[x]-=xia;d[y]+=xia;    }       st=n+1;ed=st+1;    for(int i=1;i<=n;i++) { //满足流量平衡建边,详情见无源汇有上下界可行流        if(d[i]>0)            ins(st,i,d[i]);        if(d[i]<0)            ins(i,ed,-d[i]);    }    ll sum=Dinic(); //先跑一次最大流    ins(t,s,inf);  //建无穷边    sum+=Dinic();  //再跑一次最大流    bool bk=true;    for(int k=last[st];k;k=a[k].next) { //判断是否有可行流        if(a[k].c>0) {            bk=false;break;        }    }    if(bk==false)        printf("please go home to sleep\n");    else         printf("%lld\n",a[len].c); //最后一条建的是无穷大的那条边。所以它的反向边流量就是它正向边所流过的流量    return 0;}

最后我又想吐槽一下。
loj的评测机说我第8个点超时了。结果我在本机上秒过。
有一点点恶心。。

阅读全文
0 0
原创粉丝点击