有上下界的網路流學習筆記(×)

被大佬扯去學有上下界的網絡流
拖了兩個星期之後大佬終於在我的不懈催促下開始找論文
學了半個星期了但是至今仍未找到題可做……

這是一篇好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;

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