题目链接
氧气氮气对应二维费用背包问题的体积和重量,气缸重量对应价值。
问题则是求体积和重量至少为m和n时,价值和最小。
f[i][j][k]
表示前i个物品,体积至少是j,重量至少是k的价值和最小。因此答案就是f[k][m][n]
。
可以分成两种,选第i个物品和不选第i个物品
1、不选第i个物品:f[i][j][k] = f[i-1][j][k]
2、选第i个物品:f[i-1][j - v[i]][k - m[i]] + w[i]
由于条件由经典背包问题的“不超过”,变成了“至少是”,所以和原来的问题主要有两点不同(下面用f[i][j]
表示前i个物品j容量):
1、初始化不同。
初始化只需看i=0
(一个都不选)的情况,i>0
可由i=0
递推而来。
“不超过” 的初始化要将f[0][j]
全部设为0,是因为体积不超过j,包括了体积为0的选法。
“恰好是” 的初始化只有f[0][0]=0
,其余都是无穷,因为一个都不选体积只能是0,不能是其他值。
“至少是” 的初始化只有f[0][0]=0
,其余都是无穷。因为一个都不选体积只能是0,所以只有体积至少是0才是合法的。
2、j的循环范围不同
不超过和恰好是的情况下,j - v[i] < 0
是无意义的,体积不超过-1和恰好是-1都是不可能的选法,因为体积最小就是0。
”至少是“的情况下,当j - v[i] < 0
的时候仍然可以进行状态转移,因为体积至少是-1、-2、-3也是有意义的,它包括了体积>=0的选法。可将负数的部分答案记在f[i][0]
中。
体积条件 | 写法 |
---|---|
不超过 | f全初始化为0;循环时保证j-v[i]>=0 |
恰好是 | f[0]=0 ,其余无穷;循环时保证j-v[i]>=0 |
至少是 | f[0]=0 ,其余无穷;循环时不必保证j-v[i]>=0 |
ps.初始化取正无穷还是负无穷取决于问题求最大值还是最小值,求最大取负无穷,求最小取正无穷。
#include <iostream>
#include <cstring>
using namespace std;
int m, n, k, f[22][80];
int a[1005], b[1005], c[1005];
int main() {
scanf("%d%d%d", &m, &n, &k);
for (int i = 1; i <= k; i++) {
scanf("%d%d%d", &a[i], &b[i], &c[i]);
}
memset(f, 0x3f, sizeof(f));
f[0][0] = 0;
for (int i = 1; i <= k; i++) {
for (int j = m; j >= 0; j--) {
for (int kk = n; kk >= 0; kk--) {
f[j][kk] = min(f[j][kk], f[max(j - a[i], 0)][max(kk - b[i], 0)] + c[i]);
}
}
}
printf("%d\n", f[m][n]);
}