二维费用背包问题
给定\(n\)个物品,对于每个物品有容量\(v_i\), 负收益\(w_i\),给定负收益阈值\(k\), 最大容量\(m\), 求保证负收益最低的情况
下最多能装多少物品。
该问题对于集合划分同样可以划分成装与不装两个集合,与01背包相同, 我们需要考虑两个状态量, 负收益和容量,对于保证负收益最低,我们可以通过枚举最终状态得到。
则可以列状态转移方程:
\(f[i][j][l] = max(f[i - 1][j][k], f[i - 1][j - v_i][l - k_i] + 1)\)
另外,基于\(f\)函数表达是在当前状态最多能装多少物品, 那么我们可以对\(f[n][m][x] = f[n][m][k]\)枚举,满足该等式的最小\(x\)即为最小负收益。
例题
这题需要理解题目,我们设定方程\(f[i][j][k]\) 表示考虑前\(i\)个物品,当精灵球数量不超过\(j\), 消耗\(hp\)不超过\(k\)时的最大捕获精灵数。题目中要求皮卡丘的血量不能低于0, 因此我们输出\(f[n][m][k - 1]\)即可,另外还需要保证最小\(hp\)消耗,可以从已知的状态种进行枚举, 我们枚举\(f[i][j]\)层,找到满足\(f[i][j][x] = f[n][m][k - 1]\) 的最小\(x\)即为消耗
Code
#include <bits/stdc++.h>
using i64 = long long;
const int N = 110, M = 510, K = 1010;
int n, m, k;
int dp[K][M], hp[K][M];
int v[N], h[N];
// dpijk 表示考虑前i个精灵,当背包容量为j时, 皮卡丘hp为k时,的最大精灵球数量, 并且保证皮卡丘hp最大
/*
将集合划分为,选第i个物品和不选第i个物品,则有转移方程:
则dp[i][j][k] = max(dp[i - 1][j][k], dp[i - 1][j - v[i]][k - v2[i]] + 1);
*/
int main() {
std::cin >> n >> m >> k;
for(int i = 1; i <= k; i ++) {
std::cin >> v[i] >> h[i];
}
for(int i = 1; i <= k; i ++) {
for(int j = n; j >= v[i]; j --) {
for(int l = m; l >= h[i]; l --) {
dp[j][l] = std::max(dp[j - v[i]][l - h[i]] + 1, dp[j][l]);
}
}
}
std::cout << dp[n][m - 1] << " ";
int cost = 0x3f3f3f3f;
for(int i = 0; i <= m - 1; i ++) {
if(dp[n][m - 1] == dp[n][i]) {
cost = i;
break;
}
}
// std::cout << cost << "\n";
std::cout << m - cost;
}