CF1519F Chests and Keys(最小割最大流+状压dp)
题目:洛谷CF1519F、CF1519F
题目描述:
有\(n\)个盒子,第\(i\)个盒子里面有钱\(a_i\),有\(m\)种锁与其对应的钥匙,每个锁都有无穷多个,购买第\(i\)种钥匙需要花费\(b_i\)钱,在第\(i\)个盒子上放第\(j\)个锁需要花费\(c_{i,j}\)代价
\(Alice\)和\(Bob\)在玩游戏,\(Alice\)在花费某些代价在某些盒子上放了某些锁,\(Bob\)需要购买某些钥匙去打开他能打开的箱子,并获得箱子里的钱
打开一个盒子需要打开那个盒子上的所有锁,每种钥匙只能打开它对应的锁,且可以使用多次
当\(Bob\)最终赚了钱,那么\(Bob\)赢,否则\(Alice\)赢
问要让\(Alice\)赢,\(Alice\)最少需要花费多少钱去放置锁,或者无解输出\(-1\)
蒟蒻题解:
假设我们已知\(Alice\)是怎么放锁的,让我们看看\(Bob\)获得最大的收益是多少
\(Bob\)的最大收益可以转化为:所有盒子的钱\(-\)没打开的盒子的钱\(-\)购买锁所花费的钱
要求没打开的盒子的钱\(+\)购买锁所花费的钱的最小值
让我们建网络流的图,源点\(S\)指向每个盒子,流量为对应盒子里面的钱,每个锁指向汇点\(T\),流量为购买对应钥匙需要花费的代价,如果第\(i\)个盒子放了第\(j\)个锁,那么就让第\(i\)个盒子向第\(j\)个锁连边,流量为正无穷大
这样把\(\sum_{i=1}^{n}a_i-\)最小割最大流便是\(Bob\)能获得的最大收益
当\(Bob\)输,也就是\(\sum_{i=1}^{n}a_i-\)最小割最大流\(\leq 0\),又网络流图的最大流\(\leq \sum_{i=1}^{n}a_i\),所以把\(\sum_{i=1}^{n}a_i-\)最小割最大流\(\geq 0\),如果不能赚钱,他可以什么都不买,这样收益变为\(0\),所以\(Bob\)输等价于\(\sum_{i=1}^{n}a_i=\)最小割最大流,即这张网络流的图从源点连出去的边满流
如果连向汇点的边满流都无法使从源点连出去的边满流,那么一定是无解
如果对于每种情况跑一遍网络流,那肯定是不行的
由于要求源点连出去的边要满流,观察到\(n,m,a_i,b_i\)都很小,我们可以状压\(dp\),枚举\(b\)剩下的所有状态,一共\(5^m\)种,然后对于每个源点连出去的边使得它满流,由于远远跑不满,预处理一下后,跑到的总的状态数远小于\(5^{2m}\),计算需要\(m\)的时间
时间复杂度\(\Theta(m 5^{2m})\)
参考程序:
#include<bits/stdc++.h>
using namespace std;
#define Re register int
const int Inf = 1e9;
int n, m, sum, N = 1, ans = Inf, a[8], b[8], c[8], g[8], f[8][15630];
vector<int> q[8];
inline int read()
{
char c = getchar();
int ans = 0;
while (c < 48 || c > 57) c = getchar();
while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
return ans;
}
inline int min(int x, int y)
{
return x < y ? x : y;
}
int main()
{
n = read(), m = read();
for (Re i = 1; i <= n; ++i) a[i] = read(), sum += a[i];
for (Re i = 0; i < m; ++i, N *= 5) b[i] = read(), sum -= b[i];
if (sum > 0)
{
puts("-1");
return 0;
}
f[0][0] = 0, ans, q[0].push_back(0);
for (Re i = a[1]; i < N; ++i)
{
int u = i, v = 0, w = 0, p = 1;
for (Re j = 0; j < m && u; ++j, u /= 5)
{
if (u % 5 > b[j])
{
p = 0;
break;
}
v += u % 5;
}
if (!p) continue;
for (Re j = 1; j <= n && v > 0; ++j)
{
v -= a[j];
if (!v)
{
w = j;
break;
}
}
if (w) q[w].push_back(i);
}
for (Re i = 1; i <= n; ++i)
{
for (Re j = 0; j < m; ++j) c[j] = read();
int u = q[i].size(), v = q[i - 1].size();
for (Re j = 0; j < u; ++j)
{
int s = q[i][j];
f[i][j] = Inf;
for (Re k = 0; k < m; ++k, s /= 5) g[k] = s % 5;
for (Re k = 0; k < v; ++k)
{
int s = q[i - 1][k], p = 1, ss = 0;
for (Re l = 0; l < m; ++l, s /= 5)
{
if (s % 5 > g[l])
{
p = 0;
break;
}
if (g[l] > s % 5) ss += c[l];
}
if (!p) continue;
f[i][j] = min(f[i][j], f[i - 1][k] + ss);
}
}
}
int u = q[n].size();
for (Re i = 0; i < u; ++i) ans = min(ans, f[n][i]);
printf("%d", ans);
return 0;
}

浙公网安备 33010602011771号