題目鏈接: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需要多加關注。