背包问题
约 591 字大约 2 分钟
算法动态规划
2025-02-17
01背包
问题描述:一个有容量上限背包,多种价值不同的物品,一种物品只能拿一次,求背包能够拿的最大价值。
核心思想:利用动态规划的思想,用fi,j来记录能拿i个物品,且背包容量为j时,能达到的最大价值。
如何更新fi,j:对于第i个物品,我们可以选择拿或者不拿,分别对应fi−1,j与fi−1,j−wi+vi,则可以得到状态转移方程:fi,j=max{fi−1,j,fi−1,j−wi+vi}
我们可以使用二维数组来维护fi,j,但这样做可能会MLE,注意到fi只与fi−1有关,即只与上一次的值相关,所以我们可以降低维度到一维。(但是需要注意更新数据的顺序)
for (int i = 1; i <= n; i++)
for (int l = W; l >= w[i]; l--) f[l] = max(f[l], f[l - w[i]] + v[i]);
// 先更新l大的数据,再更新l小的数据
// 因为l大的数据由l小的数据得到
// 如果先更新l小的数据,l大的数据就无法正常更新完全背包
问题描述:在01背包的基础上,每种物品能拿多次。
状态转移方程:fi,j=max{fi−1,j,fi−1,j−k×wi+k×vi},可以优化为fi,j=max{fi−1,j,fi,j−wi+vi},相当于是可以多次更新状态。
for (int i = 1; i <= n; i++)
for (int l = w[i]; l <= W; l++) f[l] = max(f[l], f[l - w[i]] + v[i]);多重背包
练习题
状态转移方程:fi,j=max{maxl=1j{fi−l,j−l+abs(bi−bi−l+1)},fi−1,j}
#include <iostream>
#include <vector>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<int> a(n + 1), b(n + 1);
auto dp = vector(n + 1, vector(k + 1, 0));
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= min(k, i); j++) {
int ans = 0;
for (int l = 1; l <= j; l++) {
ans = max(ans, dp[i - l][j - l] + abs(b[i] - a[i - l + 1]));
}
dp[i][j] = max(ans, dp[i - 1][j]);
}
}
cout << dp[n][k] << endl;
}
}