就是说有N个商人,和M个货源,然后一共是K种商品
输入第一行是N M K
接下来是N行,每行K个,表示商人对各种商品的需求情况,需求数都在[0,3]内
再接下来是M行,每行K个,表示货源中每种商品的供应情况,供应书也在[0,3]内
再接下来是K个N*M的矩阵,第x个矩阵的第y行第z列表示第x种商品从第z个货源运输到第y个商人那里需要多少花费(1个的花费)
然后问你现在想完成从货源到商人的运输,问商人们能否得到他们想要的那么多的货物,不能就输出-1,能就输出最小总花费
嗯,第一次写费用流,费好大力气读懂题之后,感觉像二分图啊,从货源到商人,但是边太多了,二分图没法做啊,不过其实图也类似于一个二分图,但是还是当普通图来对待,跑费用流,
然后,货物好多啊,怎么办,连重边的话,怎么区分呢?后来看网上的说法是对每种商品都跑一次费用流,不知道整体上一次跑的话该怎么跑
好,然后对于每种商品,先建一个超级源点,连接每个货源,容量就是这个货源的商品数,花费呢,就是0,先把货源商品数给约束了,然后建个超级汇点,连接每个商人,容量,就是商人的所需商品数,保证最后得到的商品数不会超过所以商人的所需数量,然后中间货源到商人建边,容量就为无穷,因为在超级源点和超级汇点那里已经约束了,费用就是对应的费用
呃,当然,还有判断能否提供商人所需的数量,可以在跑费用流时跑出来,但其实可以提前算出来,因为每个货源都可以给所有商人供货,所以如果货源的总商品数<商人的总需求数,肯定就是不可以,直接就是-1即可,否则就肯定能满足
然后跑k次费用流,算出答案即可
#include <string>
#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <fstream>
#include <queue>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
#define maxn 10005
#define maxm 100005
//最小费用最大流,求最大费用只需要取相反数,结果取相反数即可。
//点的总数为 N,点的编号[0,N-1]
struct Edge
{
int to, next, cap, flow, cost;
}edge[maxm];
int head[maxn], tol;
int pre[maxn], dist[maxn];
bool vis[maxn];
int N;//节点总个数,节点编号从0~N-1
void init(int n)
{
N = n;
tol = 0;
memset(head, -1, sizeof(int)*maxn);
}
void addedge(int u, int v, int cap, int cost)
{
edge[tol].to = v;
edge[tol].cap = cap;
edge[tol].cost = cost;
edge[tol].flow = 0;
edge[tol].next = head[u];
head[u] = tol++;
edge[tol].to = u;
edge[tol].cap = 0;
edge[tol].cost = -cost;
edge[tol].flow = 0;
edge[tol].next = head[v];
head[v] = tol++;
}
bool spfa(int s, int t)
{
queue<int>q;
memset(dist, 0x3f, sizeof(int)*maxn);
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(int)*maxn);
dist[s] = 0;
vis[s] = true;
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (edge[i].cap > edge[i].flow &&
dist[v] > dist[u] + edge[i].cost)
{
dist[v] = dist[u] + edge[i].cost;
pre[v] = i;
if (!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
if (pre[t] == -1)return false;
else return true;
}
//返回的是最大流,cost存的是最小费用
int minCostMaxflow(int s, int t, int &cost)
{
int flow = 0;
cost = 0;
while (spfa(s, t))
{
int Min = INF;
for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to])
{
if (Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
}
for (int i = pre[t]; i != -1; i = pre[edge[i ^ 1].to])
{
edge[i].flow += Min;
edge[i ^ 1].flow -= Min;
cost += edge[i].cost * Min;
}
flow += Min;
}
return flow;
}
int shop[55][55], place[55][55], goods[55][55][55];
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
//ios::sync_with_stdio(false);
//cin.tie(0); cout.tie(0);
//ifstream in;
//in.open("input.txt", ios::in);
int n, m, k;
while (scanf("%d%d%d", &n, &m, &k) != EOF)
{
if (n == 0 && m == 0 && k == 0)
break;
int ans = 0, sum = 0;
int t1 = 0, t2 = 0;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < k; ++j)
scanf("%d", &shop[i][j]);
}
for (int i = 0; i < m; ++i)
{
for (int j = 0; j < k; ++j)
scanf("%d", &place[i][j]);
}
for (int x = 0; x < k; ++x)
{
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
scanf("%d", &goods[x][i][j]);
}
}
for (int x = 0; x < k; ++x)
{
init(n + m + 2);
t1 = 0; t2 = 0;
for (int i = 0; i < m; ++i)
{
if (place[i][x]>0)
addedge(0, i + 1, place[i][x], 0);
t1 += place[i][x];
}
for (int i = 0; i < n; ++i)
{
if (shop[i][x]>0)
addedge(m + i + 1, m + n + 1, shop[i][x], 0);
t2 += shop[i][x];
}
if (t1 < t2)
{
ans = -1; break;
}
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
addedge(j + 1, m + i + 1, INF, goods[x][i][j]);
}
t1 = 0; t2 = 0;
t2 = minCostMaxflow(0, m + n + 1, t1);
ans += t1;
}
printf("%d\n", ans);
}
//while (1);
return 0;
}