今天题目中规中矩,主要是为了理解先背包后物品与先物品后背包的差别,这里先说结论:在对排列顺序有要求的情况下是求排列数,应当先遍历背包,再遍历物品;在对排列顺序没有要求的情况下是求组合数,应当先遍历物品,再遍历背包。
518. 零钱兑换 II
这道题属于是求组合数,所以应该先遍历物品,再遍历背包,这道题并不是求背包最大价值,而是求满足装满背包的方案数,所以这道题还是用一维dp数组来做,与之前不同的是,本题是完全背包,一个物品可以使用多次,所以可以从前往后遍历。
class Solution {
public:
int change(int amount, vector<int>& coins) {
//1.确定dp[j]的含义:在背包容量为j,下标为[0, i]的硬币组合的符合条件的个数
//2.确定递推公式 dp[j] += dp[j - coins[i]]
//3.dp数组初始化 dp[0] = 1
//4.确定遍历顺序:先物品,再背包(不能颠倒,颠倒的话算的是排列数)
//5.打印数组(省略)
int m = coins.size();
vector<int> dp(amount + 1, 0);
//初始化
dp[0] = 1;
for(int i = 0; i < m; i++){
for(int j = 1; j <= amount; j++){
if(j >= coins[i])
dp[j] += dp[j - coins[i]];
}
}
return dp[amount];
}
};
377. 组合总和 Ⅳ
这道题属于是求排列数,先遍历背包,再遍历物品,其余的不需要改动。
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
//1.确定dp[j]的含义:在背包容量为j,下标为[0, i]的数字组合的符合条件的个数
//2.确定递推公式 dp[j] += dp[j - coins[i]]
//3.dp数组初始化 dp[0] = 1
//4.确定遍历顺序:先背包,再物品(不能颠倒,颠倒的话算的是排列数)
//5.打印数组(省略)
vector<int> dp(target + 1, 0);
dp[0] = 1;
for(int j = 1; j <= target; j++){
for(int i = 0; i < nums.size(); i++){
if(j >= nums[i] && dp[j] < INT_MAX - dp[j - nums[i]])
dp[j] += dp[j - nums[i]];
}
}
return dp[target];
}
};
57. 爬楼梯(卡码网)
之前就做过这个,不过没用背包来解决,这道题目属于是求排列数,例如m = 2, n = 5,则(1, 2, 2)和(2, 1, 2)是两种不同的攀爬方案,所以这道题我们还是先背包再物品。
#include<iostream>
#include<vector>
using namespace std;
int main(){
//1.确定dp[j]的含义:在背包容量为j,下标为[0, n]的数字组合的符合条件的个数
//2.确定递推公式 dp[j] += dp[j - coins[i]]
//3.dp数组初始化 dp[0] = 1
//4.确定遍历顺序:先背包,再物品(不能颠倒,颠倒的话算的是组合数)
//5.打印数组(省略)
int m, n;
cin >> n; //楼梯阶数
cin >> m; //每次最多能爬的阶数
vector<int> dp(n + 1, 0);
dp[0] = 1;
for(int j = 1; j <= n; j++){
for(int i = 1; i <= m; i++){
if(j < i) continue;
dp[j] += dp[j - i];
}
}
cout << dp[n] << endl;
return 0;
}
今天做实验尊嘟好累啊啊啊!!!!!