题解 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\)

  1. \(j - v1 > 0\),表示要满足 \(j\) 的需求,不能只靠 \(v1\),还要选取其他的氧气瓶才能达到要求。
  2. \(j - v1 = 0\),表示要满足 \(j\) 的需求,只选取 \(v1\) 就足够了,恰好可以满足 \(j\) 的需求。
  3. \(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;
}
posted @ 2022-06-29 12:09  wbs200  阅读(90)  评论(2)    收藏  举报