题意
有$n$个商店,$m$个供货商和$k$种物品,首先$n$行$k$列的矩阵第$i$行$j$列的数字表示第$i$个商店对第$j$种物品的需求量,接下来$m$行$k$列的矩阵第$i$行$j$列的数字表示第$i$个供货商对第$j$种物品的储藏量,接下来$k$个$n$行$m$列的矩阵,第$i$个矩阵第$j$行$p$列表示从第$p$个供应商运输1个单位第$i$种物品到第$j$个商店需要的花费。问满足所有商店的物品需求前提下,最小花费是多少,若不能满足需求,输出-1。
思路
不难看出这是一道最小费用最大流的题目,可对每个商店和供应商都拆成$k$个点建图,但这样流网络的规模较大,很可能会超时。注意到$k$个物品互相是独立的,我们可以对每个物品跑最小费用流,这样就不用拆点,由于用Dijkstra改进算法求解最小费用最大流的时间复杂度为$O(FElogV)$,流网络规模缩小$k$倍后,$F,E,V$都会缩小$k$倍,虽然$k$个物品要跑$k$次,但显然总的时间复杂度仍然明显减小。建图如下:
源点向每个供应商连边,容量为供应商对当前物品的储藏量,费用为0;
每个供应商向每个商店连边,容量为无穷大,费用为当前物品从对应的供应商到对应商店需要的运输单价;
每个商店向汇点连边,容量为商店对当前物品的需求量,费用为0;
代码实现
#include <iostream> #include <cstdio> #include <vector> #include <queue> #include <cstdlib> #include <cstring> #define N 200 using namespace std; typedef pair<int, int> P; const int INF = 0x3f3f3f3f; struct Edge { int to, cap, cost, rev; Edge(int t, int c, int cc, int r) :to(t), cap(c), cost(cc), rev(r){} }; int V; vector<Edge> G[N]; int h[N]; int dist[N]; int prevv[N]; int preve[N]; int ned[60][60], sup[60][60], k_tot[60]; void addedge(int from, int to, int cap, int cost) { G[from].push_back(Edge(to, cap, cost, G[to].size())); G[to].push_back(Edge(from, 0, -cost, G[from].size() - 1 )); } int min_cost_flow(int s, int t, int f) { int res = 0; fill(h, h + V, 0); while (f > 0) { priority_queue<P, vector<P>, greater<P> >q; fill(dist, dist + V, INF); dist[s] = 0; q.push(P(0, s)); while (!q.empty()) { P p = q.top(); q.pop(); int v = p.second; if (dist[v] < p.first)continue; for (int i = 0; i < G[v].size(); i++) { Edge &e = G[v][i]; if (e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]) { dist[e.to] = dist[v] + e.cost + h[v] - h[e.to]; prevv[e.to] = v; preve[e.to] = i; q.push(P(dist[e.to], e.to)); } } } if (dist[t] == INF) return -1; for (int j = 0; j < V; j++) h[j] += dist[j]; int d = f; for (int x = t; x != s; x = prevv[x]) d = min(d, G[prevv[x]][preve[x]].cap); f -= d; res += d * h[t]; for (int x = t; x != s; x = prevv[x]) { Edge &e = G[prevv[x]][preve[x]]; e.cap -= d; G[x][e.rev].cap += d; } } return res; } int main() { int n, m, k; while (~scanf("%d %d %d", &n, &m, &k) && (n || m || k)) { V = n + m + 2; memset(k_tot, 0, sizeof(k_tot)); for (int i = 0; i < n; i++) { for (int j = 0; j < k; j++) { scanf("%d", &ned[i][j]); k_tot[j] += ned[i][j]; } } for (int i = 0; i < m; i++) { for (int j = 0; j < k; j++) { scanf("%d", &sup[i][j]); } } int s = n + m, t = n + m + 1, ans = 0; for (int i = 0; i < k; i++) { for (int j = 0; j < V; j++) G[j].clear(); for (int j = 0; j < m; j++) addedge(s, j, sup[j][i], 0); for (int j = 0; j < n; j++) addedge(m + j, t, ned[j][i], 0); for (int j = 0, cost; j < n; j++) { for (int p = 0; p < m; p++) { scanf("%d", &cost); addedge(p, j + m, INF, cost); } } if (ans == -1) continue; int tmp = min_cost_flow(s, t, k_tot[i]); if (tmp == -1) ans = -1; else ans += tmp; } printf("%d ", ans); } return 0; }