有上下界的网路流学习笔记(×)

被大佬扯去学有上下界的网络流
拖了两个星期之后大佬终于在我的不懈催促下开始找论文
学了半个星期了但是至今仍未找到题可做……

这是一篇好blog:https://www.cnblogs.com/liu-runda/p/6262832.html
(超级无敌详细!就是在看的时候差一点不认识”流“字了……)

caioj1186: 无源汇有上下界可行流(模板)

http://caioj.cn/problem.php?id=1186
负责人的blog:https://blog.csdn.net/hanks_o/article/details/77970978

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
    int x,y,c,next,other;
}a[41000];
int last[210],len;
int dep[210],list[210];
int d[21000],cs[21000],id[21000],ans[21000];//d:初始流中的关系  cs:初始流  id:原来对应的是哪条边
int st,ed,n,m;
void build(int x,int y,int c,int now)
{
    len++;int k1=len;id[len]=now;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
    len++;int k2=len;id[len]=0;
    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;
} 
bool bfs()
{
    memset(dep,0,sizeof(dep));
    dep[st]=1;list[1]=st;
    int head=1,tail=1;
    while (head<=tail)
    {
        int x=list[head];
        for (int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if (dep[y]==0&&a[k].c>0)
            {
                dep[y]=dep[x]+1;
                tail++;
                list[tail]=y; 
            }
        }
        head++;
    }
    if (dep[ed]==0) return 0;
    else return 1;
}
int dfs(int x,int flow)
{
    if (x==ed) return flow;
    int tot=0;
    for (int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if (flow-tot>0&&a[k].c>0&&dep[y]==dep[x]+1)
        {
            int sum=dfs(y,min(flow-tot,a[k].c));
            tot+=sum;a[k].c-=sum;a[a[k].other].c+=sum;
        }
    }
    if (tot==0) dep[x]=0;
    return tot;
}
int main()
{
    //流量=初始流量+附加流量
    //如果满足条件 即初始流+附加流量后能够守恒 
    //因为初始流量不一定守恒 所以附加流量也不一定守恒
    //即满足:如果初始流量的流入量=流出量+x 附加流中流出量=流入量+x
    //            初始流量的流入量=流出量-x 附加流中流出量=流入量-x
    //            初始流量满足流量守恒 附加流也满足流量守恒
    //因为附加流是无源汇不满足流量守恒 而dinic求出的是有源汇满足流量守恒  所以我们需要新建一些边来满足流量守恒后再来跑dinic 从而求出不满足流量守恒的情况 
    scanf("%d%d",&n,&m);
    memset(last,0,sizeof(last));len=0;
    memset(ans,0,sizeof(ans));
    memset(d,0,sizeof(d));
    memset(cs,0,sizeof(cs));
    memset(id,0,sizeof(id));
    for (int i=1;i<=m;i++)
    {
        int x,y,up,low;
        scanf("%d%d%d%d",&x,&y,&low,&up);
        //直接让答案为low 那么这条边可以接受的流量就是low-low~up-low 即0~up-low 
        build(x,y,up-low,i);
        d[x]-=low;d[y]+=low;//有一条x~y流量为low的边 那么原来在x的有low的流量流到了y x少了low的流量 y多了low的流量 
        cs[i]=low;
    }
    int klen=len;
    st=n+1;ed=st+1;
    for (int i=1;i<=n;i++)
    {
        //如果每个点流进来的流量能够和流出去的流量相等 即d=0 一定满足条件 如果不相等 就要把这些  
        if (d[i]>0)//如果到这个点的流量大于0 附加流量流进来的多于流出去的(初始流量中流出去的多于流进来的) 那么要让这些多的流出去的流量有一个来头 让源点给他一点流量使得他平衡 
        {
            build(st,i,d[i],0);
        }
        if (d[i]<0) //如果到这个点的流量小于0 附加流量流出去的多于流进来的 (初始流量流进来的多余于流出去的) 那么要让这些多的流进来流量有一个去处 让汇点分掉一点流量 
        {
            build(i,ed,-d[i],0);
        }
    } 
    int sum=0;
    while (bfs()) sum+=dfs(st,999999999);
    //如果流量守恒 指向st的边的流量和(d[i]>0) 和指向ed的边的流量和(d[i]<0) 应该是相等的 即 Σd[i] =0; 
    //如果st-i满流 说明能够满足i-j这些点之间的要求 
    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("NO\n");
    }
    else
    {
        printf("YES\n");
        //流过去多少流量=原来的容量-现在的容量=反向边的流量 
        for (int i=1;i<=klen;i+=2)
        {
            ans[id[i]]=a[a[i].other].c;
        }
        for (int i=1;i<=m;i++)
        {
            printf("%d\n",ans[i]+cs[i]);
        }
    }
    return 0;
}

caioj1187: 有源汇有上下界最大流(模板)

http://caioj.cn/problem.php?id=1187
负责人的blog:https://blog.csdn.net/hanks_o/article/details/77984623

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
    int x,y,c,next,other;
}a[41000];
int last[210],len;
int dep[210],list[210];
int d[21000],id[21000];
int st,ed;
void build(int x,int y,int c,int now)
{
    len++;int k1=len;id[len]=now;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
    len++;int k2=len;id[len]=0;
    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;
}
bool bfs()
{
    memset(dep,0,sizeof(dep));
    dep[st]=1;list[1]=st;
    int head=1,tail=1;
    while (head<=tail)
    {
        int x=list[head];
        for (int k=last[x];k;k=a[k].next)
        {
            int y=a[k].y;
            if (dep[y]==0&&a[k].c>0)
            {
                dep[y]=dep[x]+1;
                list[++tail]=y;
            }
        }
        head++;
    }
    if (dep[ed]==0) return 0;
    return 1;
}
int dfs(int x,int flow)
{
    if (x==ed) return flow;
    int tot=0;
    for (int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if (dep[y]==dep[x]+1&&a[k].c>0&&flow>tot)
        {
            int sum=dfs(y,min(flow-tot,a[k].c));
            tot+=sum;a[k].c-=sum;a[a[k].other].c+=sum;
        }
    }
    if (tot==0) dep[x]=0;
    return tot;
}
int main()
{
    int n,m,s,t;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for (int i=1;i<=m;i++)
    {
        int x,y,up,low;
        scanf("%d%d%d%d",&x,&y,&low,&up);
        build(x,y,up-low,i);
        d[x]-=low;d[y]+=low;
    }
    st=n+1;ed=st+1;
    int klen=len;
    for (int i=1;i<=n;i++)
    {
        if (d[i]<0)
        {
            build(i,ed,-d[i],0);
        }
        if (d[i]>0)
        {
            build(st,i,d[i],0);
        }
    }
    build(t,s,999999999,0);//汇点连到源点 相当于取消了源汇 直接按第一题做法来做 
    int sum=0;
    while (bfs()) sum+=dfs(st,999999999);
    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 
    {
        int ans=a[a[last[t]].other].c;//流到汇点的流量 
        for (int i=1;i<=len;i++) if (id[i]==0) a[i].c=0;//id0的不是反向边就是新增的边 
        last[st]=0;last[ed]=0;
        st=s;ed=t;
        while (bfs()) ans+=dfs(st,999999999);
        printf("%d",ans);
    }
    return 0;
}

caioj1188: 有源汇有上下界最小流(模板)

http://caioj.cn/problem.php?id=1188
负责人的blog:https://blog.csdn.net/hanks_o/article/details/77995557

第一种方法:

先跑一次可行流 满足下界
这时候只要在残量网络上求出最小流就好了
因为反向边的等价于正向边的减小
那么求出t到s的最大流就等效于s到t的最小流

波老师没有写第一种方法的博客 那我这里补充一下好了

//hanks_o  思路1 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
    int x,y,next,other;
    long long c;
}a[510000];
int last[51000],len;
int list[51000],dep[51000];
long long d[51000];
int id[260000];
int st,ed;
long long inf=1e10;
void build(int x,int y,long long c,int now)
{
    len++;int k1=len;id[len]=now;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
    len++;int k2=len;id[len]=0;
    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;
}
bool bfs()
{
    memset(dep,0,sizeof(dep));
    dep[st]=1;list[1]=st;
    int head=1,tail=1;
    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&&dep[y]==0)
            {
                dep[y]=dep[x]+1;
                list[++tail]=y;
            }
        }
        head++;
    }
    if (dep[ed]==0) return 0;
    return 1;
}
long long dfs(int x,long long flow)
{
    if (x==ed) return flow;
    long long tot=0;
    for (int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if (dep[y]==dep[x]+1&&a[k].c>0&&flow-tot>0)
        {
            long long sum=dfs(y,min(a[k].c,flow-tot));
            a[k].c-=sum;a[a[k].other].c+=sum;tot+=sum;
        }
    }
    if (tot==0) dep[x]=0;
    return tot;
}
int main()
{
    int n,m,s,t;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for (int i=1;i<=m;i++)
    {
        int x,y;
        long long up,low;
        scanf("%d%d%lld%lld",&x,&y,&low,&up);
        build(x,y,up-low,i);
        d[x]-=low;d[y]+=low;
    }
     st=n+1;ed=st+1;
    int klen=len;
    for (int i=1;i<=n;i++)
    {
        if (d[i]>0) build(st,i,d[i],0);
        if (d[i]<0) build(i,ed,-d[i],0);
    }
    build(t,s,inf,0);
    long long sum=0,ans;
    while (bfs()) sum+=dfs(st,inf);
    //while (bfs()) sum+=dfs(st,inf);
    ans=a[len].c;
    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 
    {
        //把t到s的边和所有st和ed的连边都删掉
        for (int k=last[st];k;k=a[k].next) a[k].c=0;
        for (int k=last[ed];k;k=a[k].next) a[k].c=0;
        last[t]=a[last[t]].next;
        last[s]=a[last[s]].next;
        a[len].c=0;a[len-1].c=0; len-=2;
        last[st]=0;last[ed]=0;
        st=t;ed=s; sum=0;
        while (bfs()) sum+=dfs(st,inf);
        printf("%lld",ans-sum);
    }
    return 0;

}

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

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

//hanks_o 思路二
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node
{
    int x,y,next,other;
    long long c;
}a[510000];
int last[51000],len;
int list[51000],dep[51000];
long long d[51000];
int id[260000];
int st,ed;
long long inf=1e10;
void build(int x,int y,long long c,int now)
{
    len++;int k1=len;id[len]=now;
    a[len].x=x;a[len].y=y;a[len].c=c;a[len].next=last[x];last[x]=len;
    len++;int k2=len;id[len]=0;
    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;
}
bool bfs()
{
    memset(dep,0,sizeof(dep));
    dep[st]=1;list[1]=st;
    int head=1,tail=1;
    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&&dep[y]==0)
            {
                dep[y]=dep[x]+1;
                list[++tail]=y;
            }
        }
        head++;
    }
    if (dep[ed]==0) return 0;
    return 1;
}
long long dfs(int x,long long flow)
{
    if (x==ed) return flow;
    long long tot=0;
    for (int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if (dep[y]==dep[x]+1&&a[k].c>0&&flow-tot>0)
        {
            long long sum=dfs(y,min(a[k].c,flow-tot));
            a[k].c-=sum;a[a[k].other].c+=sum;tot+=sum;
        }
    }
    if (tot==0) dep[x]=0;
    return tot;
}
int main()
{
    int n,m,s,t;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for (int i=1;i<=m;i++)
    {
        int x,y;
        long long up,low;
        scanf("%d%d%lld%lld",&x,&y,&low,&up);
        build(x,y,up-low,i);
        d[x]-=low;d[y]+=low;
    }
     st=n+1;ed=st+1;
    int klen=len;
    for (int i=1;i<=n;i++)
    {
        if (d[i]>0) build(st,i,d[i],0);
        if (d[i]<0) build(i,ed,-d[i],0);
    }
    long long sum=0,ans;
    while (bfs()) sum+=dfs(st,inf);
    build(t,s,inf,0);
    while (bfs()) sum+=dfs(st,inf);
    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 
    {
        ans=a[len].c;
        printf("%lld",ans);
    }
    return 0;

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章