笔记:代码随想录算法训练营day37:完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ、70. 爬楼梯 (进阶)
学习资料:代码随想录
文中含大模型生成内容
完全背包
52. 携带研究材料(第七期模拟笔试)
相比于之前的一个物品只能放一次,这次一个物品可以放多次了
递推公式变成了dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]);在计算不放该物品有几种方法时,只把该物品的空间让开就可以了
这是二维,先遍历背包还是物品都行
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n,v;
cin>>n>>v;
vector<int> weight(n,0);
vector<int> value(n,0);
for(int i=0;i<n;i++){
cin>>weight[i]>>value[i];
}
vector<vector<int>> dp(n,vector<int>(v+1,0));
for(int j=value[0];j<=v;j++){
dp[0][j] = ((j/weight[0])*value[0]);
}
for(int i=1;i<n;i++){
for(int j=0;j<=v;j++){
if(j<weight[i]) dp[i][j] = dp[i-1][j];
else{
dp[i][j] = max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
}
}
}
cout<<dp[n-1][v]<<endl;
return 0;
}
压缩到一维直接借用代码随想录的回答:
将上一层dp[i-1] 的那一层拷贝到 当前层 dp[i] ,那么 递推公式由:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])
变成: dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i])
这里有录友想,这样拷贝的话, dp[i - 1][j] 的数值会不会 覆盖了 dp[i][j] 的数值呢?
并不会,因为 当前层 dp[i][j] 是空的,是没有计算过的。
变成 dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i])
我们压缩成一维dp数组,去掉 i 层数维度。
即该点还没计算,仍是上一层的值
这道题求价值,对于一维先遍历谁也是无所谓的。
但是,无论二维还是一维,j倒序遍历不行,因为要用到本层的j了。(这里我没模拟,但感觉应该是对的)
一维:
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n,v;
cin>>n>>v;
vector<int> weight(n,0);
vector<int> value(n,0);
for(int i=0;i<n;i++){
cin>>weight[i]>>value[i];
}
vector<int> dp(v+1,0);
for(int i=0;i<n;i++){
for(int j=0;j<=v;j++){
if(j>=weight[i]){
dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
}
}
}
cout<<dp[v]<<endl;
return 0;
}
518.零钱兑换II
力扣题目链接
求有几种方法
class Solution {
public:
int change(int amount, vector<int>& coins) {
// 定义
vector<vector<uint64_t>> dp(coins.size(),vector<uint64_t>(amount+1,0));
// 初始化
for(int i=0;i<coins.size();i++){ //遇到重定义的报错先排查一下是不是i后面写的逗号
dp[i][0] = 1;
}
for(int j=coins[0];j<=amount;j++){
if(j%coins[0]==0) dp[0][j] = 1;
}
// 遍历
for(int i =1;i<coins.size();i++){
for(int j=1;j<=amount;j++){
if(j<coins[i]) dp[i][j] = dp[i-1][j];
else{
dp[i][j] = dp[i-1][j]+dp[i][j-coins[i]];
}
}
}
return dp[coins.size()-1][amount];
}
};
对于一维,就只能先遍历物品了。为什么二维可以先遍历背包而一维不行?j-coins可能会被覆盖掉,举例coin=1,2,amount=3,根据递推公式dp[j]+=dp[j-coins[i]]和dp[i][j]=dp[i-1][j]+dp[i][j-coins[i]]对比来看,外层是j,一维在物品0时用的j-coins[i]是处理过其他物品后的dp值
class Solution {
public:
int change(int amount, vector<int>& coins) {
// 定义
vector<uint64_t> dp(amount+1,0);
// 初始化
for(int j=0;j<=amount;j++){
if(j%coins[0]==0) dp[j] = 1;
}
// dp[0] = 1;
// 遍历
for(int i =1;i<coins.size();i++){
for(int j=0;j<=amount;j++){
if(j>=coins[i]){
dp[j] = dp[j]+dp[j-coins[i]];
}
}
}
return dp[amount];
}
};
377. 组合总和 Ⅳ
力扣题目链接
求的是排列数,这时就是一维先遍历背包求得的了,让它帮我模拟一下:
感觉这里就暂时摒弃和求组合的方法的比较,单纯从求排列的角度去理解更清晰
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<uint64_t> dp(target+1,0);
dp[0] = 1;
for(int j=0;j<=target;j++){
for(int i =0;i<nums.size();i++){
if(j>=nums[i]) dp[j]=dp[j]+dp[j-nums[i]];
}
}
return dp[target];
}
};
70. 爬楼梯(进阶版)
卡码网:57. 爬楼梯
相当于求的还是方法数
物品重量就是台阶数
#include <iostream>
#include <vector>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
vector<int> dp(n+1,0);
dp[0]=1;
for(int j=0;j<=n;j++){
for(int i=1;i<=m;i++){
if(j>=i) dp[j] +=dp[j-i];
}
}
cout<<dp[n];
return 0;
}