P4015 运输问题

\(\color{#0066ff}{题目描述}\)

W 公司有 m 个仓库和 n 个零售商店。第 i 个仓库有 \(a_i\) 个单位的货物;第 j 个零售商店需要 \(b_j\) 个单位的货物。

货物供需平衡,即\(\sum\limits_{i=1}^{m}a_i=\sum\limits_{j=1}^{n}b_j\)

从第 i 个仓库运送每单位货物到第 j 个零售商店的费用为 \(c_{ij}\)​ 。

试设计一个将仓库中所有货物运送到零售商店的运输方案,使总运输费用最少。

\(\color{#0066ff}{输入格式}\)

第 1 行有 2 个正整数 m 和 n,分别表示仓库数和零售商店数。

接下来的一行中有 m 个正整数 \(a_i\),表示第 i 个仓库有 \(a_i\) 个单位的货物。

再接下来的一行中有 n 个正整数 \(b_j\),表示第 j 个零售商店需要 \(b_j\) 个单位的货物。

接下来的 m 行,每行有 n 个整数,表示从第 i 个仓库运送每单位货物到第 j 个零售商店的费用 \(c_{ij}\)

\(\color{#0066ff}{输出格式}\)

两行分别输出最小运输费用和最大运输费用。

\(\color{#0066ff}{输入样例}\)

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

\(\color{#0066ff}{输出样例}\)

48500
69140

\(\color{#0066ff}{数据范围与提示}\)

1≤n,m≤100

\(\color{#0066ff}{题解}\)

S向每个仓库连容量为仓库货物量,边权为0的边(保证每个仓库的流出量)
仓库向商店连容量为仓库货物量,边权为运输费用的边
每个商店向T连容量为所需量,边权为0的边(保证所需)
建正边跑Dinic,在取相反数重新来
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#define _ 0
#define LL long long
inline LL in()
{
    LL x=0,f=1; char ch;
    while(!isdigit(ch=getchar()))(ch=='-')&&(f=-f);
    while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
    return x*f;
}
const LL inf=99999999999999999LL;
struct node{int to,nxt; LL dis,cap;}e[105050];
int cnt=1;
std::queue<int> q;
LL dis[555],change[555];
int n,m,s,t;
int head[555],road[555];
LL vala[555],valb[555];
bool vis[555];
LL val[555][555];
inline void add(int from,int to,LL cap,LL dis)
{
    cnt++;
    e[cnt].to=to;
    e[cnt].dis=dis;
    e[cnt].cap=cap;
    e[cnt].nxt=head[from];
    head[from]=cnt;
}
inline bool spfa()
{
    for(int i=s;i<=t;i++) dis[i]=inf,change[i]=inf,vis[i]=0;
    dis[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int tp=q.front(); q.pop();
        vis[tp]=false;
        for(int i=head[tp];i;i=e[i].nxt)
        {
            int go=e[i].to;
            if(dis[go]>dis[tp]+e[i].dis&&e[i].cap>0)
            {
                dis[go]=dis[tp]+e[i].dis;
                road[go]=i;
                change[go]=std::min(change[tp],e[i].cap);
                if(!vis[go])
                {
                    vis[go]=true;
                    q.push(go);
                }
            }
        }
    }
    return change[t]!=inf;
}
inline void mcmf(int flag)
{
    LL flow=0,cost=0;
    while(spfa())
    {
        flow+=change[t];
        cost+=change[t]*dis[t];
        for(int o=t;o!=s;o=e[road[o]^1].to)
        {
            e[road[o]].cap-=change[t];
            e[road[o]^1].cap+=change[t];
        }
    }
    printf("%lld\n",cost*flag);
}
int main()
{
    m=in(),n=in();
    t=n+m+1,s=0;
    for(int i=1;i<=m;i++) add(s,i,vala[i]=in(),0),add(i,s,0,0);
    for(int i=1;i<=n;i++) add(i+m,t,valb[i]=in(),0),add(t,i+m,0,0);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            val[i][j]=in();
            add(i,j+m,vala[i],val[i][j]);
            add(j+m,i,0,-val[i][j]);
        }
    mcmf(1);
    cnt=1;
    for(int i=s;i<=t;i++) head[i]=0;
    for(int i=1;i<=m;i++) add(s,i,vala[i],0),add(i,s,0,0);
    for(int i=1;i<=n;i++) add(i+m,t,valb[i],0),add(t,i+m,0,0);
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        {
            add(i,j+m,vala[i],-val[i][j]);
            add(j+m,i,0,val[i][j]);
        }
    mcmf(-1);
    return 0;
}

转载于:https://www.cnblogs.com/olinr/p/10098170.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值