题目概述
市场上有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 11 1 1
3
2
200 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