这题还是挺裸的
当然 是用最小费最为方便。建图一目了然
KM麻烦那么一点,就是每个物品要拆成一份一份的。
建图的时候注意,KM匹配的模板一定是左边的点数不大于右边的点数 至少我的模板是这样的
在本题中,要求供应商提供的东西要满足商店的需求,不能满足就直接输出-1, 所以理论上商店的点应该比供应商少,商店的点应该放在左边,供应商放在右边
由于是最小权匹配,所以我这里用了一个比较大的数减去权值来处理,而不是直接取负数
先是一个最小费的
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 111
#define MAXM 55555
#define INF 100000007
using namespace std;
struct EDGE
{
int v, cap, cost, next, re; // re记录逆边的下标。
} edge[MAXM];
int n, m, ans, flow, src, des;
int e, head[MAXN];
int que[MAXN], pre[MAXN], dis[MAXN];
bool vis[MAXN];
void init()
{
e = ans = flow = 0;
memset(head, -1, sizeof(head));
}
void add(int u, int v, int cap, int cost)
{
edge[e].v = v;
edge[e].cap = cap;
edge[e].cost = cost;
edge[e].next = head[u];
edge[e].re = e + 1;
head[u] = e++;
edge[e].v = u;
edge[e].cap = 0;
edge[e].cost = -cost;
edge[e].next = head[v];
edge[e].re = e - 1;
head[v] = e++;
}
bool spfa()
{
int i, h = 0, t = 1;
for(i = 0; i <= n; i ++)
{
dis[i] = INF;
vis[i] = false;
}
dis[src] = 0;
que[0] = src;
vis[src] = true;
while(t != h)
{
int u = que[h++];
h %= n;
vis[u] = false;
for(i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].v;
if(edge[i].cap && dis[v] > dis[u] + edge[i].cost)
{
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
que[t++] = v;
t %= n;
}
}
}
}
if(dis[des] == INF) return false;
return true;
}
void end()
{
int u, p, mi = INF;
for(u = des; u != src; u = edge[edge[p].re].v)
{
p = pre[u];
mi = min(mi, edge[p].cap);
}
for(u = des; u != src; u = edge[edge[p].re].v)
{
p = pre[u];
edge[p].cap -= mi;
edge[edge[p].re].cap += mi;
ans += mi * edge[p].cost; // cost记录的为单位流量费用,必须得乘以流量。
}
flow += mi;
}
int nt, k;
int shop[55][55], sup[55][55];
int money;
int main()
{
while(scanf("%d%d%d", &nt, &m, &k) != EOF)
{
if(!nt && !m && !k) break;
int res = 0;
n = nt + m + 2;
src = nt + m + 1;
des = nt + m + 2;
for(int i = 1; i <= nt; i++)
for(int j = 1; j <= k; j++)
scanf("%d", &shop[i][j]);
for(int i = 1; i <= m; i++)
for(int j = 1; j <= k; j++)
scanf("%d", &sup[i][j]);
int flag = 1;
for(int q = 1; q <= k; q++)
{
init();
for(int i = 1; i <= nt; i++)
for(int j = 1; j <= m; j++)
{
scanf("%d", &money);
add(j, i + m, INF, money);
}
int fk = 0;
for(int i = 1; i <= m; i++)
add(src, i, sup[i][q], 0);
for(int i = 1; i <= nt; i++)
add(i + m, des, shop[i][q], 0), fk += shop[i][q];
while(spfa()) end();
res += ans;
if(flow != fk) flag = 0;
}
if(flag)
printf("%d\n", res);
else printf("-1\n");
}
return 0;
}
然后用KM做的
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define eps 1e-5
#define MAXN 555
#define MAXM 55555
#define INF 100000007
using namespace std;
int n, m, ny, nx;
int w[MAXN][MAXN];
int lx[MAXN], ly[MAXN];
int linky[MAXN];
int visx[MAXN], visy[MAXN];
int slack[MAXN];
bool find(int x)
{
visx[x] = 1;
for(int y = 1; y <= ny; y++)
{
if(visy[y]) continue;
int t = lx[x] + ly[y] - w[x][y];
if(t == 0)
{
visy[y] = 1;
if(linky[y] == -1 || find(linky[y]))
{
linky[y] = x;
return true;
}
}
else if(slack[y] > t) slack[y] = t;
}
return false;
}
int KM()
{
memset(linky, -1, sizeof(linky));
for(int i = 1; i <= nx; i++) lx[i] = -INF;
memset(ly, 0, sizeof(ly));
for(int i = 1; i <= nx; i++)
for(int j = 1; j <= ny; j++)
if(w[i][j] > lx[i]) lx[i] = w[i][j];
for(int x = 1; x <= nx; x++)
{
for(int i = 1; i <= ny; i++) slack[i] = INF;
while(true)
{
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if(find(x)) break;
int d = INF;
for(int i = 1; i <= ny; i++)
if(!visy[i]) d = min(d, slack[i]);
if(d == INF) return -1;
for(int i = 1; i <= nx; i++)
if(visx[i]) lx[i] -=d;
for(int i = 1; i <= ny; i++)
if(visy[i]) ly[i] += d;
else slack[i] -= d;
}
}
int cnt = 0;
for(int i = 1; i <= ny; i++)
if(linky[i] != -1) cnt++;
if(cnt != nx) return -1;
int tp = 0;
for(int i = 1; i <= ny; i++)
if(linky[i] != -1 ) tp += w[linky[i]][i] - 200;
return -tp;
}
int nt, k;
int shop[55][55], sup[55][55];
int money[55][55];
int ha[555], hb[555];
int main()
{
while(scanf("%d%d%d", &nt, &m, &k) != EOF)
{
if(!nt && !m && !k) break;
for(int i = 1; i <= nt; i++)
for(int j = 1; j <= k; j++)
scanf("%d", &shop[i][j]);
for(int i = 1; i <= m; i++)
for(int j = 1; j <= k; j++)
scanf("%d", &sup[i][j]);
int flag = 1, res = 0;
for(int q = 1; q <= k; q++)
{
memset(w, 0, sizeof(w));
for(int i = 1; i <= nt; i++)
for(int j = 1; j <= m; j++)
scanf("%d", &money[i][j]);
nx = 0, ny = 0;
for(int i = 1; i <= nt; i++)
for(int j = 1; j <= shop[i][q]; j++)
{
nx++;
ha[nx] = i;
}
for(int i = 1; i <= m; i++)
for(int j = 1; j <= sup[i][q]; j++)
{
ny++;
hb[ny] = i;
}
for(int i = 1; i <= nx; i++)
for(int j = 1; j <= ny; j++)
w[i][j] = 200 - money[ha[i]][hb[j]];
int tp = KM();
if(tp == -1) flag= 0;
else res += tp;
}
if(flag)
printf("%d\n", res);
else printf("-1\n");
}
return 0;
}