定义:dp[i][j]表示从下标为[0-i]的物品,每个物品可以取无限次,放进容量为j的背包,价值总和最大是多少。
递推公式:
- 不放物品i:背包容量为j,里面不放物品i的最大价值是
dp[i - 1][j]。 - 放物品i:背包空出物品i的容量后,背包容量为
j - weight[i],dp[i][j - weight[i]]为背包容量为j - weight[i]且不放物品i的最大价值,那么dp[i][j - weight[i]] + value[i](物品i的价值),就是背包放物品i得到的最大价值 递推公式:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]);(注意,完全背包二维dp数组 和 01背包二维dp数组 递推公式的区别,01背包中是dp[i - 1][j - weight[i]] + value[i]))
初始化:
dp[0][j],即:存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
那么很明显当 j < weight[0]的时候,dp[0][j]应该是 0,因为背包容量比编号0的物品重量还小。
当j >= weight[0]时,dp[0][j] 如果能放下weight[0]的话,就一直装,每一种物品有无限个。
AC代码:
#include <iostream>
#include <math.h>
#include <numeric>
#include <string>
#include <vector>
using namespace std;
int main() {
int n = 0;
int v = 0;
cin >> n >> v;
// vector<vector<int>> dp(n, vector<int>(v + 1, 0));
vector<int> dp(v + 1, 0);
vector<int> value(n);
vector<int> weight(n);
for (int k = 0; k < n; k++) {
cin >> weight[k] >> value[k];
}
for (int i = 0; i < n; ++i) {
for (int j = weight[i]; j <= v; ++j) {
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
}
}
cout << dp[v];
return 0;
}
2. 518. 零钱兑换 II
注意初始化;注意使用二维dp时,当j小于coins[i],dp[i][j] 要赋值为dp[i - 1][j]
当使用一维dp时: 如果求组合数就是外层for循环遍历物品,内层for遍历背包。 如果求排列数就是外层for遍历背包,内层for循环遍历物品。
class Solution {
public:
int change(int amount, vector<int>& coins) {
int n = coins.size();
// 0-i的硬币有几种方法组成j大小金额
vector<vector<uint64_t>> dp(n, vector<uint64_t>(amount + 1));
for (int j = 0; j <= amount; j += coins[0]) {
dp[0][j] = 1;
}
for (int i = 0; i < n; ++i) {
dp[i][0] = 1;
}
for (int i = 1; i < n; ++i) {
for (int j = 0; j <= amount; ++j) {
if (coins[i] > j) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
}
}
}
return dp[n - 1][amount];
}
};
class Solution {
public:
int change(int amount, vector<int>& coins) {
int n = coins.size();
vector<uint64_t> dp(amount + 1);
dp[0] = 1;
// 先物品,后背包,就是求组合数
// 假设:coins[0] = 1,coins[1] = 5。
// 那么就是先把1加入计算,然后再把5加入计算,
// 得到的方法数量只有{1, 5}这种情况。而不会出现{5, 1}的情况。
// **所以这种遍历顺序中dp[j]里计算的是组合数!**
for (int i = 0; i < n; ++i) {
for (int j = coins[i]; j <= amount; ++j) {
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
}
};
dp[j]表示0-i的数字有几种构成j的组合
题目要求组合内数字顺序不同也算不同的组合,如:(1, 1, 2)和(1, 2, 1),因此实际按排列来做
代码如下:
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
int n = nums.size();
vector<uint64_t> dp(target + 1);
dp[0] = 1;
for (int j = 0; j <= target; ++j) {
for (int i = 0; i < n; ++i) {
if (j < nums[i])
continue;
dp[j] += dp[j - nums[i]];
}
}
return dp[target];
}
};
#include <iostream>
#include <math.h>
#include <numeric>
#include <string>
#include <vector>
using namespace std;
int main() {
int n = 0;
int m = 0;
cin >> n >> m;
// 每次至多爬j阶楼梯的情况下,爬到i阶有多少种方法
vector<int> dp(n + 1, 0);
dp[0] = 1;
// 求排列,先遍历背包,再遍历物品
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (j > i)
continue;
dp[i] += dp[i - j];
}
}
cout << dp[n];
return 0;
}
```# 算法训练1-day30-动态规划