网络流2

这篇博客探讨了三种网络流问题的解决方案,包括无源无汇有上下界可行流的构造、有上下界有源汇最大流问题以及有上下界有源有汇最小流问题。通过建立超级源点和超级汇点,结合最大流算法来判断是否存在可行流或求解最大、最小流。同时,介绍了如何在解法中添加附加源点和汇点,以及如何还原可行流和最小流。
摘要由CSDN通过智能技术生成

sgu194
题意:无源无汇有上下界可行流构造
解法:建立超级源点S和超级汇点T
1.S指向每个点,权值是进入每个点的下界和
2.每个点指向T,权值是从每个点出去的权值和
3.每条边的权值改为上界-下界
求S->T的最大流,判断从S出发的边是否满流,如果满流,则存在可行流。
一个可行流为u->v的流量+u->v的下界

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

const int INF = 0x3f3f3f3f;
const int maxn = 200+10;
const int maxm = 40000+10;
int n,m;
struct node{
    int v,nxt,cap,flow;
}e[maxm<<2];
int head[maxn],tot,S,T;
int in[maxn],out[maxn];
int down[maxm];

void add(int u,int v,int cap){
    e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
}

int d[maxn],used[maxn];

bool BFS(){
    queue<int> q;
    for(int i=S;i<=T;i++) d[i]=-1;
    d[S]=0,q.push(S);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v;
            if(d[v]==-1&&e[i].cap-e[i].flow>0){
                d[v]=d[u]+1,q.push(v);
            }
        }
    }
    return d[T]!=-1;
}

int DFS(int u,int a){
    if(u==T||a==0) return a;
    int f=0,m;
    for(int i=used[u];i!=-1;i=used[u]=e[i].nxt){
        int v=e[i].v;
        if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow)))>0){
            e[i].flow+=m,e[i^1].flow-=m;
            f+=m,a-=m;
            if(!a) break;
        }
    }
    return f;
}

int mf(int S,int T){
    int ans=0;
    while(BFS()){
        for(int i=S;i<=T;i++) used[i]=head[i];
        ans+=DFS(S,INF);
    }
    return ans;
}

int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(head,-1,sizeof(head)); tot=0;
        S=0,T=n+1;
        memset(in,0,sizeof(in)); memset(out,0,sizeof(out));
        for(int i=1;i<=m;i++){
            int u,v,l,r;
            scanf("%d%d%d%d",&u,&v,&l,&r);
            in[v]+=l,out[u]+=l,down[i]=l;
            add(u,v,r-l),add(v,u,0);
        }
        for(int i=1;i<=n;i++){
            if(in[i]>0) add(S,i,in[i]),add(i,S,0);
            if(out[i]>0) add(i,T,out[i]),add(T,i,0);
        }
        mf(S,T);
        bool flag=true;
        for(int i=head[S];i!=-1;i=e[i].nxt){
            if(e[i].cap-e[i].flow>0){
                flag=false; break;
            }
        }
        if(!flag) printf("NO\n");
        else{
            printf("YES\n");
            for(int i=0;i<2*m;i+=2) printf("%d\n",e[i].flow+down[i/2+1]);
        }
    }
    return 0;
}

zoj3229
题意:n天分别给m个人拍照,并给出一些限制条件
1.每天只能给特定的Ki个人拍照,且张数给定[Lij,Rij]
2.每天拍照总数为Di
3.每个人n天共拍照张数不少于Gi
求n天最多拍多少张照片?
解法:m个人的照片分配到n天拍摄,建立含源点和汇点的二分图。左边m个点表示m个人,右边n个点表示n天,每个人每天分配照片数量有上下界,每个人/每天的照片总数也有上下界,因此题目划归为有上下界有源汇最大流
1.建立原图+add(T,S,INF),add(S,T,0)
2.添加附加源点汇点st和ed,如上题建图
3.mf(st,ed),判断是否存在可行流
4.head[st]=head[ed]=-1(删除附加源点和汇点),mf(S,T),即为最大流答案
5.还原最大流与上题还原可行流类似

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

const int INF = 0x3f3f3f3f;
const int maxn = 2000+10;
const int maxm = 1000000+10;
int n,m;
int in[maxn],out[maxn],down[maxm];
struct node{
    int v,nxt,cap,flow;
}e[maxm];
int head[maxn],tot,S,T;
int st,ed;
int dd[maxn];
void add(int u,int v,int cap){
    e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
}

int d[maxn],used[maxn];

bool BFS(int S,int T){
    queue<int> q;
    for(int i=0;i<=T;i++) d[i]=-1;
    d[S]=0,q.push(S);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v;
            if(d[v]==-1&&e[i].cap-e[i].flow>0){
                d[v]=d[u]+1,q.push(v);
            }
        }
    }
    return d[T]!=-1;
}

int DFS(int u,int a,int T){
    if(u==T||a==0) return a;
    int f=0,m;
    for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){
        int v=e[i].v;
        if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow),T))>0){
            e[i].flow+=m,e[i^1].flow-=m;
            f+=m,a-=m;
            if(!a) break;
        }
    }
    return f;
}

int mf(int S,int T){
    int ans=0;
    while(BFS(S,T)){
        for(int i=0;i<=T;i++) used[i]=head[i];
        ans+=DFS(S,INF,T);
    }
    return ans;
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        S=0,T=n+m+1;
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        memset(head,-1,sizeof(head)); tot=0;
        for(int i=1;i<=m;i++){
            int g; scanf("%d",&g);
            add(S,i,INF-g),add(i,S,0);
            in[i]+=g,out[S]+=g;
        }
        int cnt=0,cnt2=tot;
        for(int i=1;i<=n;i++){
            int c,u,l,r;
            scanf("%d%d",&c,&dd[i]);
            for(int j=1;j<=c;j++){
                scanf("%d%d%d",&u,&l,&r); u++;
                down[cnt++]=l,in[i+m]+=l,out[u]+=l;
                add(u,i+m,r-l),add(i+m,u,0);
            }
        }
        for(int i=1;i<=n;i++){
            add(i+m,T,dd[i]),add(T,i+m,0);//无下界,不需要加入out和in数组中
        }
        add(T,S,INF),add(S,T,0);
        st=T+1,ed=T+2;
        int sum=0;
        for(int i=S;i<=T;i++){
            if(in[i]>0) sum+=in[i],add(st,i,in[i]),add(i,st,0);
            if(out[i]>0) add(i,ed,out[i]),add(ed,i,0);
        }
        int ans=mf(st,ed);
        if(ans!=sum){
            printf("-1\n");
        }
        else{
            head[st]=head[ed]=-1;//删除超级源点和超级汇点
            printf("%d\n",mf(S,T));
            for(int i=0;i<cnt;i++){
                printf("%d\n",e[cnt2+2*i].flow+down[i]);
            }
        }
        printf("\n");
    }
    return 0;
}

sgu176
题意:有上下界有源有汇最小流
解法:
1.建立原图(容量为上界-下界)
2.添加附加源点和汇点,建立这两点到各点的边
3.求ans1=mf(附加源点,附加汇点)
4.添边汇点->源点,容量INF
5.求ans2=mf(附加源点,附加汇点)
6.判断ans1+ans2与附加汇点流量是否相等,不等无可行流,否则存在可行流。且最小流为ans2,流量即为原图边的流量+流量下界

#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;

const int maxn = 200+10;
const int maxm = 10000+10;
const int INF = 0x3f3f3f3f;
int n,m;
int in[maxn],out[maxn],down[maxm],sum;
struct node{
    int v,nxt,cap,flow;
}e[maxm<<2];
int head[maxn],tot;
int d[maxn],used[maxn];
int S,T,st,ed;
void add(int u,int v,int cap){
    e[tot].v=v,e[tot].nxt=head[u],e[tot].cap=cap,e[tot].flow=0,head[u]=tot++;
}

bool BFS(int S,int T,int num){
    queue<int> q;
    memset(d,0,sizeof(d));
    d[S]=1,q.push(S);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=e[i].nxt){
            int v=e[i].v;
            if((!d[v])&&e[i].cap>e[i].flow) d[v]=d[u]+1,q.push(v);
        }
    }
    return d[T];
}
int DFS(int u,int a,int T){
    if(u==T||a==0) return a;
    int f=0,m;
    for(int i=used[u];i!=-1;used[u]=i=e[i].nxt){
        int v=e[i].v;
        if(d[v]==d[u]+1&&(m=DFS(v,min(a,e[i].cap-e[i].flow),T))>0){
            e[i].flow+=m,e[i^1].flow-=m;
            f+=m,a-=m;
            if(!a) break;
        }
    }
    return f;
}

int mf(int S,int T,int num){
    int ans=0;
    while(BFS(S,T,num)){
        for(int i=0;i<=num;i++) used[i]=head[i];
        ans+=DFS(S,INF,T);
    }
    return ans;
}

int main(){
        //freopen("a.txt","r",stdin);
        scanf("%d%d",&n,&m);
        S=1,T=n,st=n+1,ed=n+2;
        memset(head,-1,sizeof(head)); tot=0;
        memset(in,0,sizeof(in)),memset(out,0,sizeof(out));
        for(int i=1;i<=m;i++){
            int u,v,c,d;
            scanf("%d%d%d%d",&u,&v,&c,&d);
            if(d) in[v]+=c,out[u]+=c,down[i]=c,add(u,v,0),add(v,u,0);
            else add(u,v,c),add(v,u,0),down[i]=0;
        }
        sum=0;
        for(int i=1;i<=n;i++){
            if(in[i]>0) add(st,i,in[i]),add(i,st,0),sum+=in[i];
            if(out[i]>0) add(i,ed,out[i]),add(ed,i,0);
        }
        int ans1=mf(st,ed,ed);
        add(T,S,INF),add(S,T,0);
        int ans2=mf(st,ed,ed);
        if(ans1+ans2!=sum){
            printf("Impossible\n"); return 0;
        }
        printf("%d\n",ans2);
        for(int i=0;i<2*m;i+=2){
            if(i!=2*m-2) printf("%d ",e[i].flow+down[i/2+1]);
            else printf("%d\n",e[i].flow+down[i/2+1]);
        }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值