题意:
N个销售店 标号为1 - N,M个供货店,1 - M,
然后如果其中一个店主订购商品,应该安排那个供应商向店主提供多少商品
以减少费用
从不同的供应地点向不同的店主运输不同种类的单件货物的成本可能不同
已知:
每个供应地点K种存货,N个店主的K种货物的订单
以及不同的供应地点到不同的店主运输不同种类的货物的成本
求如何安排货物供应以最小化运输总成本
输入
3 个数 N M K
N 行 店主的订单 每行包含K个整数 (0 <= K <= 3)
表示店主需要的商品数量
M行 给出供应地点的存储,每行K个整数,表示存储在该供应地点的货物数量
然后是 K个整数矩阵
(每个都有大小为 N * M), 第i行的整数范围(0,100),
第i行的第j列表示成本将第 K 个货物的一个单位从第j个供应地运到第 i 个店主
输入3个0结束
输出
满足所有店主的所有需求,则在一行中打印一个整数,这是最低成本;
否则只输出“-1”。
数据范围:
时间:4 S
N M K < 50
思路:
1)第一眼看题,就感觉像图,供应商是点,销售商是点,
然后把每个点再拆成K个边(因为有K个物品)
2)建边 源点->每个供应商 (流量为供应的物品, 费用0)
每个销售商->汇点 (流量是所销售的所有物品, 费用0)
供应商每个点->给它自己提供的货物连线 (流量为每种货物的数量,费用为0)
销售商销售的货物->销售商 (流量为每种物品需要的量,费用0)
供应商提供的货物 ->每个销售商需要的货物都连线 (流量为INF, 费用为单价)
3)上面的操作太复杂而且,边太多关建图就TLE了
下面给出另一种思路
1)我们每种物品跑一次费用流(对每一种物品求最小花费),
这样建图的复杂度就下降了
2)建边,源点->每个供应商的第K种物品 (流量为供应的物品, 费用0)
每个销售商的第K种物品->汇点 (流量是所销售的所有物品, 费用0)
就把每种供应商的第K种物品(点) -> 每个销售商的第K个物品
(流量INF,费用为Cost )
3)+个物品供给不足判断
4)输入要读完,不然 会WA
AC:
#include
#include
#include
#include
#include
#include
#include
#define Del(a, b) memset(a, b, sizeof(a));
using namespace std;
const int maxn = 505;
const int inf = 0x3f3f3f3f;
int N, M, K;
int path[maxn], dis[maxn], head[maxn], vis[maxn], cnt;
int Cost[maxn][maxn];
void init() {
cnt = 0;
memset(head, -1, sizeof(head));
}
struct ac{
int v, flow, cost, nex;
}edge[maxn << 8];
void addEdge(int u, int v, int flow, int cost) {
edge[cnt] = {v, flow, cost, head[u]};
head[u] = cnt++;
edge[cnt] = {u, 0, -cost, head[v]};
head[v] = cnt++;
}
int Spfa(int s, int t) {
memset(vis, 0, sizeof(vis));
memset(dis, inf, sizeof(dis));
memset(path, -1, sizeof(path));
queue que;
que.push(s);
dis[s] = 0;
vis[s] = 1;
while(!que.empty()) {
int u = que.front();
que.pop();
vis[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].nex){
int v = edge[i].v;
int flow = edge[i].flow;
int cost = edge[i].cost;
if (flow > 0 && dis[v] > dis[u] + cost) { // 按费用增广
dis[v] = dis[u] + cost;
path[v] = i;
if(vis[v]) continue;
vis[v] = 1;
que.push(v);
}
}
}
return dis[t] != inf;
}
int MCMF(int s, int t, int &cost) {
int maxflow = 0;
while(Spfa(s, t)) {
int flow = inf;
for (int i = path[t]; i != -1; i = path[edge[i ^ 1].v]) {
flow = min(flow, edge[i].flow);
}
for (int i = path[t]; i != -1; i = path[edge[i^1].v]) {
edge[i].flow -= flow;
edge[i^1].flow += flow;
cost += flow * edge[i].cost;
}
maxflow += flow;
}
return maxflow;
}
int Sal[70][70];
int Sup[70][70];
int main() {
//freopen("in.txt", "r", stdin);
while (scanf("%d%d%d", &N, &M, &K) && N && M && K) {
memset(Sal, 0, sizeof(Sal));
memset(Sup, 0, sizeof(Sup));
for (int i = 1; i <= N; ++i) {
for(int k = 1; k <= K; ++k) {
scanf("%d", &Sal[i][k]);
Sal[0][k] += Sal[i][k];
}
}
for(int i = 1; i <= M; ++i) {
for(int k = 1; k <= K; ++k) {
scanf("%d", &Sup[i][k]);
Sup[0][k] += Sup[i][k];
}
}
bool flag = 1;
int Ans = 0;
for(int k = 1; k <= K; ++k) {
if (Sup[0][k] < Sal[0][k]) { //物品供给不足
flag = 0;
break;
}
}
for(int k = 1; k <= K; ++k) {
init();
//建立源点到供应商的点 (流量为 当前物品的总数,费用0)
for(int i = 1; i <= M; ++i) addEdge(0, i, Sup[i][k], 0);
//建立销售商到汇点的点 (流量为当前物品的总数, 费用为0)
for(int i = 1; i <= N; ++i) addEdge(i + M, M + N + 1, Sal[i][k], 0);
int cost;
//建立当前物品的数量 从 供应商j 到 销售商i 的运输费用(流量为inf, 费用为 cost)
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= M; ++j) {
scanf("%d", &cost);
addEdge(j, M + i, inf, cost);
}
}
if(flag){ //因为数据必须要读完,不然会WA,物品供给不足,就只读数据,不再跑费用流
int MF = MCMF(0, M + N + 1, Ans);
}
}
if (flag) printf("%d\n", Ans);
else printf("-1\n");
}
return 0;
}