题目链接:https://www.acwing.com/problem/content/description/1022/
首先这道题目和普通的01背包看起来不一样,因为一般的01背包通常是闲置容量然后求价值的最大数值,而这道题目则是让我们需要满足2个一定的价值然后去选择最小的花费,价值可以超过预计的价值。
这里我直接开了三维,下面是解释:
f[i, j, k]:表示在1 - i这些罐子里面选,然后满足氧气j,氮气k的最小花费。
状态转移方程:
- 不选第i个罐子, 那么f[i, j, k] = f[i - 1, j, k];但是注意这里需要加限制条件,因为我们必须要满足当前j,k的需求,所以我们在一开始还需要去维护一下加入前面1 - i - 1所有罐子都选的两个数值sum_n, sum_m只有当sum_n >= j && sum_m >= k 的时候我们才可以不去选择第I个罐子。
- 选择第i个罐子,此时f[i, j, k] = min(f[i, j, k], f[i - 1, j - a, k - b]);同时这里也需要加限制条件,加入当前sum_n + a < j && sum_m + b < k,那么表示即使选择了第i个罐子也不能满足需求,此时我们应该跳出内层的2层循环,让i ++;还有一种情况当sum_n + a >= j && sum_m + b < k的时候我们只需要跳出sum_m的最内存循环即可;还有一点也需要注意,j - a 和 k - b 可能回小于0。
代码一份:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1005, M = 100;
int f[N][M][M];
int main(void) {
// freopen("in.txt", "r", stdin);
int n, m;
scanf("%d%d", &n, &m);
int q;
scanf("%d", &q);
memset(f, 0x3f, sizeof f);
for(int i = 0; i <= q; i ++) f[i][0][0] = 0;
int sum_n = 0, sum_m = 0;
for(int i = 1; i <= q; i ++) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
int flag = 0;
for(int j = 0; j <= n; j ++) {
for(int k = 0; k <= m; k ++) {
//不选第i个
if(sum_n >= j && sum_m >= k) f[i][j][k] = f[i - 1][j][k];
//选第i个
if(sum_n + a >= j && sum_m + b < k) {
break;
} else if(sum_n + a < j && sum_m + b < k) {
flag = 2;
break;
}
f[i][j][k] = min(f[i][j][k], f[i - 1][j - a < 0?0:j - a][k - b < 0?0:k - b] + c);
}
if(flag == 2) break;
}
sum_n += a, sum_m += b;
}
printf("%d\n", f[q][n][m]);
// fclose(stdin);
return 0;
}
总结:通过这道题目,我发现有些题目可能变一下就不知道怎么做了,这个时候我们需要冷静的去进行分析,看是不是加一些限制条件或者去维护更多的东西去进行一些变化,还有需要注意临界情况,对0需要多加关注。
来源:CSDN
作者:Michel 123
链接:https://blog.csdn.net/weixin_42596275/article/details/104318238