运输问题2

本文介绍了一个涉及多个城市的商品运输问题,通过构建特定图模型解决最大流问题,包括如何处理带有上下界的流量限制,并提供了一段C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【问题描述】
一个工厂每天生产若干商品,需运输到销售部门进行销售。从产地到销地要经过某些城镇,有不同的路线可以行走,每条两城镇间的公路都有一定的流量限制。为了保证公路的运营效率,每条公路都有一个容量下界,也就是至少应有多少车辆通过。每条公路还有一个容量上界,也就是最多应有多少车辆通过。请你计算,在不考虑其它车辆使用公路的前提下,如何充分利用所有的公路,使产地运输到销地的商品最多,最多能运输多少商品。
【输入格式】
输入文件有若干行
第一行,一个整数n,表示共有n个城市(2<=n<=100),产地是1号城市,销地是n号城市
下面有n行,每行有2n个数字。第p行第2q−1,2q列的数字表示城镇p与城镇q之间有无公路连接。数字为0表示无,大于0表示有公路,且这两个数字分别表示该公路流量的下界,上界。
【输出格式】
输出文件有一行
第一行,1个整数ans,表示最大流量为ans
【输入输出样例】
输入文件名: maxflowb.in
6
0 0 1 3 0 10 0 0 0 0 0 0
0 0 0 0 0 0 5 7 0 0 0 0
0 0 0 0 0 0 0 0 2 8 0 0
0 0 0 0 1 3 0 0 0 0 3 5
0 0 2 4 0 0 0 0 0 0 2 6
0 0 0 0 0 0 0 0 0 0 0 0
输出文件名:maxflowb.out
10

裸的有汇源上下界最大流

建图:

以前写的最大流默认的下界为0,而这里的下界却不为0,所以我们要进行再构造让每条边的下界为0,这样做是为了方便处理。对于每根管子有一个上界容量up和一个下界容量low,我们让这根管子的容量下界变为0,上界为up-low。可是这样做了的话流量就不守恒了,为了再次满足流量守恒,即每个节点”入流=出流”,我们增设一个超级源点st和一个超级终点sd。我们开设一个数组du[]来记录每个节点的流量情况。

du[i]=in[i](i节点所有入流下界之和)-out[i](i节点所有出流下界之和)。

当du[i]大于0的时候,st到i连一条流量为du[i]的边。

当du[i]小于0的时候,i到sd连一条流量为-du[i]的边。

最后连一条st到sd的边,容量为INF

跑最大流就行

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=100010;
const int maxm=1000010;
const int INF=1000000000;
int cnt,fir[maxn],to[maxm],nxt[maxm],cap[maxm];
int dis[maxn],gap[maxn],path[maxn],fron[maxn];
queue<int>q;

struct Net_Flow{
    int tot;
    void Init(int tot_){
        memset(dis,0,sizeof(dis));
        memset(gap,0,sizeof(gap));
        memset(fir,0,sizeof(fir));
        cnt=1;tot=tot_;
    }
    void add(int a,int b,int c){
        nxt[++cnt]=fir[a];
        cap[cnt]=c;
        fir[a]=cnt;
        to[cnt]=b;
    }
    void addedge(int a,int b,int c){
        add(a,b,c);add(b,a,0);
    }
    bool BFS(int S,int T){
        dis[T]=1;q.push(T);
        while(!q.empty()){
            int x=q.front();q.pop();
            for(int i=fir[x];i;i=nxt[i])
                if(!dis[to[i]]){
                    dis[to[i]]=dis[x]+1;
                    q.push(to[i]);
                }
        }
        return dis[S];
    }
    int Max_Flow(int S,int T){
        if(!BFS(S,T))return 0;
        for(int i=1;i<=tot;i++)fron[i]=fir[i];
        for(int i=1;i<=tot;i++)gap[dis[i]]+=1;
        int ret=0,p=S,f,Min;
        while(dis[S]<=tot){
            if(p==T){
                f=INF;
                while(p!=S){
                    f=min(f,cap[path[p]]);
                    p=to[path[p]^1];
                }ret+=f;p=T;
                while(p!=S){
                    cap[path[p]]-=f;
                    cap[path[p]^1]+=f;
                    p=to[path[p]^1];
                }
            }

            for(int &i=fron[p];i;i=nxt[i])
                if(cap[i]&&dis[to[i]]==dis[p]-1)
                    {path[p=to[i]]=i;break;}

            if(!fron[p]){Min=tot;
                if(--gap[dis[p]]==0)break;
                for(int i=fir[p];i;i=nxt[i])
                    if(cap[i])Min=min(Min,dis[to[i]]);
                gap[dis[p]=Min+1]+=1;fron[p]=fir[p];
                if(p!=S)p=to[path[p]^1];    
            }       
        }
        //if(!Judge())ret=0;
        return ret;
    }
}ISAP;

int Min[110][110];
int Max[110][110];
int main(){
    freopen("maxflowb.in","r",stdin);
    freopen("maxflowb.out","w",stdout);
    int n,S,T,ss,tt;
    scanf("%d",&n);S=1;ss=n+1;
    ISAP.Init(n+2);T=n;tt=n+2;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d%d",&Min[i][j],&Max[i][j]);
            ISAP.addedge(i,j,Max[i][j]-Min[i][j]);
        }
    }
    ISAP.addedge(n,1,INF);
    for(int i=1;i<=n;i++){
        int ta=0,tb=0;
        for(int j=1;j<=n;j++){
            ta+=Min[j][i];
            tb+=Min[i][j];
        }
        if(ta>tb)ISAP.addedge(ss,i,ta-tb);
        else ISAP.addedge(i,tt,tb-ta);
    }ISAP.Max_Flow(ss,tt);
    printf("%d\n",ISAP.Max_Flow(S,T));
    return 0;
}

如果是求无源汇上下界可行流

不要加最后那条边
对(st,sd)求一次最大流即可,当所有附加边全部满流时(即maxflow==所有du[]>0之和),有可行解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值