[图论] NKOJ 1952 运输问题

P1952【线性规划与网络流24题 17】运输问题
时间限制 : 10000 MS   空间限制 : 65536 KB
问题描述

W公司有m个仓库和n 个零售商店。第i 个仓库有ai个单位的货物;第j个零售商店需要bj个单位的货物。
货物供需平衡,即sigma(ai)==sigma(bj)。
从第i个仓库运送每单位货物到第j个零售商店的费用为Cij。试设计一个将仓库中所有货物运送到零售商店的运输方案,
使总运输费用最少。

输入格式

第1行有2 个正整数m和n,分别表示仓库数和零售商店数。
接下来的一行中有m个正整数ai,1≤i≤m,表示第i个仓库有ai个单位的货物。
再接下来的一行中有n个正整数bj,1≤j≤n,表示第j个零售商店需要bj个单位的货物。
接下来的m行,每行有n个整数,表示从第i个仓库运送每单位货物到第j个零售商店的费用Cij。

输出格式

程序运行结束时,将计算出的最少运输费用和最多运输费用输出

样例输入

2 3
220 280
170 120 210
77 39 105
150 186 122

样例输出

48500 
69140

提示

1<=n,m<=100
0<=Ai,Bi,Cij<=1000


一道裸题,最小费用最大流和最大费用最大流的结合。

题解:

#include<cstdio>
#include<cstdlib>
#include<queue>
#include<memory.h>
#define MAX 1000
#define inf 1e8
#define reset(x) memset(x,0,sizeof(x))
using namespace std;
int t1[MAX],t2[MAX],b[MAX][MAX],c[MAX][MAX],w[MAX][MAX],path[MAX],max_flow,min_cost;
int dis[MAX],pts;
int flag[MAX];
int T,s=1;
queue<int>q;
void addflow()  //修改路径值
{  
    int flow,i;  
    flow=inf;i=T;  
    while(i!=s)  
    {  
        if(flow>c[path[i]][i])flow=c[path[i]][i];  
        i=path[i];  
    }  
    max_flow+=flow;  
    min_cost+=flow*dis[T];  
    i=T;  
    while(i!=s)  
    {  
        c[path[i]][i]-=flow;  
        c[i][path[i]]+=flow;  
        i=path[i];  
    }  
}  
bool find_path(int s) //SPFA
{  
    int i;  
    for(i=1;i<=T;i++){dis[i]=inf;path[i]=-1;flag[i]=false;}  
    dis[s]=path[s]=0;  
    flag[s]=true;  
    q.push(s);  
    while(q.size())  
    {  
        int j=q.front();
        q.pop();
        flag[j]=false;  
        for(i=1;i<=T;i++)
        {
            if((c[j][i]>0)&&(dis[i]>dis[j]+w[j][i]))  
            {  
                dis[i]=dis[j]+w[j][i];  
                path[i]=j;  
                if(!flag[i])  
                {  
                    flag[i]=true;  
                    q.push(i);  
                }  
            }  
        }
    }  
    if(dis[T]<inf)return true;  
    else return false;  
}  
int main()
{
    int m,n;
    scanf("%d %d",&m,&n);
    T=pts=m+n+2;
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&t1[i]);
        c[1][i+1]=t1[i];
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&t2[i]);
        c[1+m+i][T]=t2[i];
        /*代价默认为0,不需要重置*/ 
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&b[i][j]);
            w[i+1][m+j+1]=b[i][j];
            w[m+j+1][i+1]=-b[i][j];
            c[i+1][1+m+j]=inf;
        }
    }
    while(find_path(s)==true)
    {
        addflow();
    }
    printf("%d\n",min_cost);
    max_flow=0;
    min_cost=0; 
    reset(dis);
    reset(path);
    reset(w);
    reset(c);
    for(int i=1;i<=m;i++)
    {
        c[1][i+1]=t1[i];
    }
    for(int i=1;i<=n;i++)
    {
        c[1+m+i][T]=t2[i]; 
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
        w[i+1][m+j+1]=-b[i][j];
        w[m+j+1][i+1]=b[i][j];
        c[i+1][1+m+j]=inf;
        }
    }
    while(find_path(s)==true)
    {
        addflow();
    }
    printf("%d",-min_cost);
    return 0;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
垃圾运输问题的解决 (一) 问题重述 某城区有36个垃圾集中点,每天都要从垃圾处理厂(第37号节点)出发将垃圾运回。现有一种载重6吨的运输车。每个垃圾点需要用10分钟的时间装车,运输车平均速度为40km/h(夜里运输,不考虑塞车现象);每台车每日平均工作4小时。运输车重载运费1.8元/吨公里;运输车和装垃圾用的铲车空载费用0.4元/公里 1. 要投入多少辆运输车,每台车的行走路线,方案的运营总费用 2. 要投入多少辆铲车,每台铲车的行走路线,铲车的运营费用 3. 如果有载重量为4吨,6吨,8吨三种运输车,应该怎样调度 (二) 基本假设 1. 运输车行走拐弯的时间,路上的意外事故的耽搁时间忽略。 2. 各垃圾点的垃圾必须当天及时清除完,不允许滞留 3. 晚上9:00后不堵车 4. 每天各垃圾点的垃圾量基本相同 5. 每个垃圾点无论其中垃圾是否清理完全都需要10分钟装车时间 6. 每个垃圾点都在路口,便于垃圾的集中、运输 7. 垃圾只在晚上运输,基本保证运完后,当天不会再有新的垃圾产生 (三) 基本变量,符号和用语 |A| 表示A点到原点的距离,恒正 |B| 表示B点到原点的距离,恒正 |A-B| 表示A,B两点之间的距离,恒正 Ta 表示A点所在地的垃圾量 Spend 花费钱的数量 Time 花费的时间 装的足够多 运输车当前的载重离限载不大于0.55吨(垃圾点的最小垃圾量) 序数号 所在点的编号 父点 本点的上一点 子点 本点的下一点 (四) 问题分析和数学模型的建立 垃圾运输问题最终可以归结为最优路径搜索问题,但注意到此图为森林而不是树,不能直接套用Krusal,Prim等现成算法,于是根据具体问题设计出随机下山法,用计算模拟搜索,可以搜寻到令人满意的可行解。 先注意到两点的情况,设两点分别为A(x1,y1),B(x2,y2)。 主要有以下两种情况: 一. A,B明显有先后次序。--递减状态(如图1)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值