poj 2516

题目概述

市场上有K种货物流通,编号1到K,有N家店,编号1到N,每家都需要一定量的各种货,M家供货商,编号1到M,每家库存有一定量的各种货,从供货商运货到店面会产生运费,问供货商库存能否满足商店的货物需求,若能满足,求最小运输费用

时限

4000ms/12000ms

输入

第一行整数N,M,K,其后N行,每行K个整数,为该商店需要的各种货数量,货按编号升序给出,商店按编号升序给出,其后M行,每行K个整数,为该供货商库存各种货数量,按升序给出,其后K个N行M列的矩阵,行号代表商店,列号代表供货商,值为供货商运送一单位该种货物到商店的运费,按货号升序给出矩阵,输入到N=M=K=0结束

限制

0< N,M,K<50

输出

每行一个数,若无法满足需求,为-1,否则为最小运费

样例输入

1 3 3
1 1 1
0 1 1
1 2 2
1 0 1
1 2 3
1 1 1
2 1 1

1 1 1
3
2
20

0 0 0

样例输出

4
-1

讨论

图论,网络流,费用流,最小增广路算法,这个题说的非常直白,一看就知道是费用流,构图上也几乎没有难度,源点,供货商,商店,汇点,每种货物单独考虑,单独构图,源点到供货商残量为库存量,费用0,供货商到商店残量无穷大,费用就是单位货物的运费,这点需要稍微留心,其反向边残量0,费用为运费的相反数,不是0,像额这种刚入门的很难发现这条错误,商店到汇点残量为需求量,费用0,其他货物类别同理构图,当供不应求时,这张图的最大流会小于所有商店对该货物需求的和,由此进行判断
实现层面上,没有什么难度,只是由于输入比较奇怪,读入的时候需要稍微细心一点
虽说开个三维数组相当浪费,但是着实是非常方便,加上算法也不是什么高级货色,暂且如此

题解状态

5112K,391MS,C++,1878B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 54
#define memset0(a) memset(a,0,sizeof(a))

int N, M, K, S, T;//商店数 供货商数 货类别数 源点 汇点
int R[MAXN][MAXN * 2][MAXN * 2], W[MAXN][MAXN * 2][MAXN * 2], dis[MAXN * 2], from[MAXN * 2], require[MAXN];//残量矩阵 费用矩阵 最短路长度 父节点 每种货物的总需求量
queue<int>q;
bool inq[MAXN * 2];
int bellman_ford(int K, int N)//第一个参数是货的编号 第二个是图上节点总数
{
    int least = 0, most = 0;//最小费用 最大流
    while (1) {//下面就是bellman_ford算法 稍微改一下就可以
        int flow = INF;
        for (int p = 0; p < N; p++)
            dis[p] = INF;
        dis[S] = 0;
        q.push(S);
        while (!q.empty()) {
            int a = q.front();
            q.pop();
            inq[a] = 0;
            for (int p = 0; p < N; p++)
                if (R[K][a][p] && dis[p] > dis[a] + W[K][a][p]) {//得有残量才能松弛
                    dis[p] = dis[a] + W[K][a][p];
                    from[p] = a;
                    flow = min(flow, R[K][a][p]);
                    if (!inq[p]) {
                        q.push(p);
                        inq[p] = 1;
                    }
                }
        }
        if (dis[T] == INF) {//当没有增广路时
            if (most < require[K])//供不应求
                return -1;
            return least;
        }
        most += flow;
        least += flow*dis[T];
        for (int p = T; p; p = from[p]) {
            int i = from[p];
            R[K][i][p] -= flow;
            R[K][p][i] += flow;//顺原路返回进行增广
        }
    }
}
int fun()
{
    T = M + N + 1;//构造的汇点 源点是0
    for (int p = 1; p <= N; p++)
        for (int i = 0; i < K; i++) {
            scanf("%d", &R[i][M + p][T]);//input//商店到汇点
            require[i] += R[i][M + p][T];
        }
    for (int p = 1; p <= M; p++)
        for (int i = 0; i < K; i++)
            scanf("%d", &R[i][S][p]);//input//读入源点到供货商
    for (int p = 0; p < K; p++)
        for (int i = 1; i <= N; i++)
            for (int u = 1; u <= M; u++) {
                scanf("%d", &W[p][u][M + i]);//input//读入供货商到商店
                W[p][M + i][u] = -W[p][u][M + i];//构造反向边
                R[p][u][M + i] = INF;
            }
    int cost = 0;
    for (int p = 0; p < K; p++) {
        int add = bellman_ford(p, M + N + 2);
        if (add == -1)
            return -1;
        cost += add;
    }
    return cost;
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt", "w", stdout);

    while (~scanf("%d%d%d", &N, &M, &K) && (N || M || K)) {//input
        printf("%d\n", fun());//output
        memset0(R);
        memset0(W);
        memset0(require);
    }
}

EOF

展开阅读全文
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值