题解 SP181 SCUBADIV - Scuba diver & AcWing 1020. 潜水员
二维费用经典背包问题。
状态数组 f[i][j][k] 描述为在前 \(i\) 个物品中选取,且氧气含量至少为 \(j\),氮气含量至少为 \(k\) 的所有选法。数组含义为所有符号条件的选法的重量最小值。
每一个状态可以分为两种情况,一种是不包含第 \(i\) 个物品,那么 f[i][j][k] 的状态与 \(i - 1\) 的状态 f[i - 1][j][k] 相同,表示没有选取第 \(i\) 个物品。
另外一种状态就是包含第 \(i\) 个物品,它的状态转移方程就是 f[i][j][k] = f[i - 1][j - v1][k - v2] + w。
可以注意到,这时候 \(j\) 表示的是氧气至少是 \(j\) 的情况,\(j\) 的值和 \(v1\) 的值并没有什么直接的联系,只要原来的值加上 \(v1\) 这个值之后大于等于 \(j\) 即可,也就是说状态转移方程中的 \(j−v1\) 也就没有确切的值,它只是一个小于 \(j\) 的数,可以大于 \(0\),可以等于 \(0\),也可以小于 \(0\)。
- \(j - v1 > 0\),表示要满足 \(j\) 的需求,不能只靠 \(v1\),还要选取其他的氧气瓶才能达到要求。
- \(j - v1 = 0\),表示要满足 \(j\) 的需求,只选取 \(v1\) 就足够了,恰好可以满足 \(j\) 的需求。
- \(j - v1 < 0\),表示要满足 \(j\) 的需求,只选取 \(v1\) 就足够了,甚至还有剩余,可以大于 \(j\) 的需求,不过过剩并没有什么用,我们只需要 \(j\) 的需求就可以了。
这就导致在设置初始值的时候要考虑到 \(j−v1\) 小于 \(0\) 的时候,也是符合要求的一种情况,表示只选取 \(v1\) 这个氧气瓶,这时候除去 \(v1\) 之后,f[j] 就表示没有 \(j\) 的这一维状态下没有选择氧气瓶了,也就是 f[0] 的状态。
由此可得状态转移方程为 f[i][j][k] = f[i - 1][max(j - v1, 0)][max(k - v2, 0)] + w。表示 \(j−v1\) 至多到 \(0\) 状态。由于 \(j - v1\) 可能小于 \(0\),所以要和 \(0\) 取一个 \(\max\) 值。
#include <iostream>
#include <cstring>
using namespace std;
const int N = 22, M = 80;
int f[N][M];
int main()
{
int q;
cin >> q;
while (q--)
{
int n, m, K;
cin >> n >> m >> K;
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
while (K--)
{
int v1, v2, w;
cin >> v1 >> v2 >> w;
for (int i = n; i >= 0; i--)
for (int j = m; j >= 0; j--)
f[i][j] = min(f[i][j], f[max(0, i - v1)][max(0, j - v2)] + w);
}
cout << f[n][m] << endl;
}
return 0;
}

浙公网安备 33010602011771号