POJ 2516
题意:
n
个店主,m
个供货商(仓库),k
种商品- 给出
n
个店主的k
种商品的需求,m
个仓库存放k
种商品的个数,和对于每种商品
k
i
k_i
ki来说从
m
i
m_i
mi运送到
n
i
n_i
ni的运费 - 求最小花费, 如果商品不足则输出
-1
思路:
- 分
k
次计算:每个商品分别求最小花费,然后加和,建立二分图后首先判断商品数量是否足够给店主.然后裸KM()
存数据+建图
shop[i][k]
存第i
个店主的第
k
i
k_i
ki种商品的需求个数,sup[i][k]
存第i
个仓库存的第
k
i
k_i
ki中商品的个数.cost[t][i][j]
存商品t
从仓库j
运到店主i
的花费
- 对于第
k
i
k_i
ki种商品: 将店主需要的所有该种商品的个数拆成二分图的左部分的点,仓库拥有的所有该商品拆成二分图的右部分的点,
- 边换成负值套用KM求最大带权匹配
using namespace std;
const int maxn = 55;
const int inf = -0x3f3f3f3f;
int shop[maxn][maxn],sup[maxn][maxn],cost[maxn][maxn][maxn];
int n,m,k,cntA,cntB,delta;
int belongA[maxn*3],belongB[maxn*3];
int w[maxn*3][maxn*3],la[maxn*3],lb[maxn*3],match[maxn*3];
bool va[maxn*3],vb[maxn*3];
void read() {
for (int i=1; i<=n; ++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]);
for (int t=1; t<=k; ++t)
for (int i=1; i<=n; ++i)
for (int j=1; j<=m; ++j)
scanf("%d",&cost[t][i][j]);
}
int dfs(int u) {
va[u] = 1;
for (int i=1; i<=cntB; ++i) {
if (!vb[i]) {
if (la[u]+lb[i] - w[u][i] == 0) {
vb[i] = 1;
if (!match[i] || dfs(match[i])) {
match[i] = u;
return 1;
}
} else delta = min(delta,la[u]+lb[i]-w[u][i]);
}
}
return 0;
}
int KM () {
for (int i=0; i<maxn*3; ++i) la[i] = -inf;
memset(lb,0,sizeof lb);
for (int i=1; i<=cntA; ++i)
for (int j=1; j<=cntB; ++j)
la[i] = max(la[i],w[i][j]);
memset(match,0,sizeof match);
for (int i=1; i<=cntA; ++i) {
while (1) {
memset(va,0,sizeof va);
memset(vb,0,sizeof vb);
delta = inf;
if (dfs(i)) break;
for (int i=1; i<=cntB; ++i) {
if (va[i]) la[i] -= delta;
if (vb[i]) lb[i] += delta;
}
}
}
int ans = 0;
for (int i=1; i<=cntB; ++i)
if (match[i]) ans += w[match[i]][i];
return ans;
}
int main() {
freopen("1.in","r",stdin);
while (scanf("%d%d%d",&n,&m,&k) != EOF && (n+m+k)) {
read();
// 判断仓库的货物是否足够
bool flag = 1;
for (int i=1; i<=k; ++i) {
int need =0, have = 0;
for (int j=1; j<=n; ++j)
need += shop[j][i];
for (int j=1; j<=m; ++j)
have += sup[j][i];
if (need > have) {
flag = 0;
printf("-1\n");
break;
}
}
if (!flag) continue;
int ans = 0;
// KM分别处理每个货物
for (int t=1; t<=k; ++t) {
cntA = cntB = 0;
for (int i=1; i<=n; ++i)
for (int j=1; j<=shop[i][t]; ++j)
belongA[++cntA] = i; //给同一种货物的所有货物标号顺便 标记属于哪个店家
for (int i=1; i<=m; ++i)
for (int j=1; j<=sup[i][t]; ++j)
belongB[++cntB] = i; //存储点的货物属于哪个仓库,并且给每个相同货物标记号码
for (int i=1; i<=cntA; ++i)
for (int j=1; j<=cntB; ++j)
w[i][j] = -cost[t][belongA[i]][belongB[j]];
ans += KM();
}
printf("%d\n",-ans);
}
return 0;
}