一、题目理解

对于“考虑前\(i\)个物品,氧气体积大于等于\(j\),氮气体积大于等于\(k\)的方案”的集合(\(f[i][j][k]\))

可以按照“是否选择第\(i\)个物品”来划分

(1)若不选择第\(i\)个物品

则就是“考虑前\(i-1\)个物品,氧气体积大于等于\(j\) 并且氮气体积大于等于\(k\)的方案”的集合 (\(f[i-1][j][k]\))

(2)若选择了第\(i\)个物品

由于该集合均包含第\(i\)个物品

假设第\(i\)个物品有\(v_1\)个氧气 \(v_2\)个氮气

则除去第\(i\)个物品

就是考虑“从前\(i-1\)个物品选 氧气体积大于等于\(j-v_1\) 氮气体积大于等于\(k-v_2\)的方案”的集合

若\(j-v_1\)小于零的话

就说明第\(i\)个物品提供的氧气已经足够

不需要从前\(i-1\)个物品中获得任何氧气

因此当\(j-v_1\)小于零的时候

直接按照\(0\)考虑即可

氮气也一样

二、求最大值最小值初始化总结

二维情况

1、体积不超过\(j\),\(f[i,k] = 0\),\(0 <= i <= n, 0 <= k <= m\)(只会求价值的最大值)

并且当\(j-v<0\)时,状态没有意义。

2、体积恰好\(j\),

当求价值的最小值:\(f[0][0] = 0\), 其余是\(INF\),当\(j-v<0\)时,状态没有意义。

当求价值的最大值:\(f[0][0] = 0\), 其余是\(-INF\),当\(j-v<0\)时,状态没有意义。

3、体积至少\(j\),\(f[0][0] = 0\),其余是\(INF\)(只会求价值的最小值),状态有意义,需要从\(f[0]\)转移即可。

一维情况

1、体积不超过\(j\),\(f[i] = 0, 0 <= i <= m\)(只会求价值的最大值)

并且当\(j-v<0\)时,状态没有意义。

2、体积恰好\(j\),

当求价值的最小值:\(f[0] = 0\), 其余是\(INF\)

当求价值的最大值:\(f[0] = 0\), 其余是\(-INF\)

3、体积至少\(j\),\(f[0] = 0\),其余是\(INF\)(只会求价值的最小值),状态有意义,需要从\(f[0]\)转移即可。

二、实现代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1010; //气缸的个数上限
const int M = 100; //需要的氧气、氮气上限值
int V1; //氧需要的量
int V2; //氮需要的量
int n; //气缸的个数
int v1[N], v2[N], w[N];//第i个气缸里的氧和氮的容量及气缸重量
int f[M][M]; //DP数组
int main() {
//读入氧需要的量,氮需要的量,气缸的个数
cin >> V1 >> V2 >> n;
//读入每个气缸的氧和氮的容量及气缸重量
for (int i = 1; i <= n; i++) cin >> v1[i] >> v2[i] >> w[i];
//求最小值要把除初始状态以外的所有状态初始化为+∞
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
//因为采用了降维,所以体积都是倒着的~
for (int i = 1; i <= n; i++)
for (int j = V1; j >= 0; j--)
for (int k = V2; k >= 0; k--)
f[j][k] = min(f[j][k], f[max(j - v1[i], 0)][max(k - v2[i], 0)] + w[i]);
//输出
printf("%d", f[V1][V2]);
return 0;
}