代码随想录——动态规划01背包
1.代码随想录-逆波兰式、滑动窗口最大值2.代码随想录-栈与队列-有效的括号(括号匹配)3.代码随想录——栈与队列8-前K个高频元素4.二叉树的递归遍历和迭代遍历5.代码随想录——二叉树-11.完全二叉树的节点个数6.代码随想录——二叉树-12.平衡二叉树7.代码随想录——二叉树17-路径总和8.代码随想录——二叉树19.最大二叉树9.代码随想录——二叉树21、合并二叉树(附:递归算法复杂度分析)10.代码随想录——二叉树23、验证二叉搜索树11.代码随想录——25二叉搜索树的最小绝对值差(递归遍历如何记录前后两个指针)12.代码随想录——25.二叉搜索树中的众数13.代码随想录——26、二叉(搜索)树的最近公共祖先14.代码随想录——回溯8、组合总和II15.代码随想录——回溯9.分割回文串16.代码随想录——回溯19重新安排行程17.代码随想录——回溯 N皇后18.代码随想录——贪心8.跳跃游戏II19.代码随想录——贪心9.K次取反后最大化的数组和 && std::sort函数的第三个参数说明20.代码随想录——贪心13.分发糖果21.代码随想录——贪心算法:根据身高重建队列 & Vector原理22.代码随想录——贪心算法22单调递增的数字23.代码随想录——贪心23监控二叉树24.代码随想录——动态规划5.周总结25.代码随想录——动态规划9不同的二叉搜索树
26.代码随想录——动态规划01背包
27.代码随想录——动态规划13.分割等和子集28.代码随想录——动态规划14最后一块石头的重量II(01背包)29.动态规划——dp的含义归类(完全背包和01背包区别)30.动态规划——26单词拆分31.代码随想录——动态规划背包问题总结32.代码随想录——动态规划31打家劫舍III(树状DP)33.代码随想录——动态规划、股票问题34.代码随想录——单调栈35.回溯总结暴力:每一件物品其实只有两个状态,取或者不取,所以可以使用回溯法搜索出所有的情况,那么时间复杂度就是O(2^n),这里的n表示物品数量。
所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!
二维dp数组01背包
-
确定dp数组及下标含义
dp[i][j]表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。
此时最后返回dp[n-1][w]即可 -
确定递推公式
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
解释:从“不放入第i件物品”与“放入第i件物品的价值 + 放入前i-1件物品到容量为j-weight[i]的背包可获得的最大价值”中选最大值 -
dp数组初始化
- 容量为0时,都为0
- 因为dp[i][j]需要用到dp[i-1][j],所以需要初始化第一行。
当j>=weight[0]时,dp[0][j]应该是value[0];否则是0.
关于初始化,一定要和dp数组的定义吻合,否则到递推公式的时候就会越来越乱。
- 确定遍历顺序
先遍历物品再遍历容量 或相反都可以。因为递推公式用到的都是dp[i][j]左上的元素。
代码
#include<iostream>
#include<vector>
using namespace std;
int main(){
int M,N;
cin>>M>>N;
vector<int> value(M);
vector<int> cost(M);
for(int i=0;i<M;i++){
cin>>cost[i];
}
for(int i=0;i<M;i++){
cin>>value[i];
}
vector<vector<int>> dp(M,vector<int>(N+1,0));//dp[M][N]是从0到M-1个材料中选择,容量为N时的最大价值
//初始化
for(int j=cost[0];j<=N;j++){
dp[0][j] = value[0];
}
for(int i=1;i<M;i++){
for(int j=0;j<=N;j++){
if(j<cost[i])dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i-1][j],dp[i-1][j-cost[i]]+value[i]);
}
}
cout<< dp[M-1][N];
return 0;
}
一维dp数组01背包
二维递推公式dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
发现如果把dp[i - 1]那一层拷贝到dp[i]上,表达式完全可以是:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i]);
区别在于遍历顺序。
- 容量只能从大到小遍历。
如果从小到大,上一层的dp[j]会被覆盖,导致后续计算dp[i][j-weight[i]]时使用的是这一层的dp而出现错误。如果从大到小遍历,因为递推公式不会用到 - 只能先遍历物品再遍历容量
因为容量从大到小遍历,如果先遍历容量再遍历物品。算不出答案。
#include<iostream>
#include<vector>
using namespace std;
int main(){
int M,N;
cin>>M>>N;
vector<int> value(M);
vector<int> cost(M);
for(int i=0;i<M;i++){
cin>>cost[i];
}
for(int i=0;i<M;i++){
cin>>value[i];
}
// 一维数组
vector<int> dp(N+1,0);
//初始化
for(int j=cost[0];j<=N;j++){
dp[j] = value[0];
}
//遍历顺序——容量从后往前
for(int i=1;i<M;i++){
for(int j=N;j>=cost[i];j--){
dp[j] = max(dp[j],dp[j-cost[i]]+ value[i]);
}
}
cout << dp[N];
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!