《dp补卡——01背包问题》
目录
- 01背包
- [416. 分割等和子集](https://leetcode-cn.com/problems/partition-equal-subset-sum/)
- [1049. 最后一块石头的重量 II](https://leetcode-cn.com/problems/last-stone-weight-ii/)
- [494. 目标和](https://leetcode-cn.com/problems/target-sum/)
01背包
1、dp数组以及下标含义
dp[i][j]
标识从下标为[0,1]
的物品里任意取,放进容量为j
的背包,价值总和最大是多少?
2、确定递推公式
dp[i][j]
可以由两个方向推出:
1、dp[i-1][j]
,背包容量为j
,里面不放入物品i
的最大价值,此时dp[i][j] = dp[i-1][j]
2、dp[i-1][i-weight[i]]
推出,背包容量为i-weight[i]
的时候此时dp[i][j] = dp[i-1][j-weight[i]]+valuep[i]
;
所以递推公式为:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
3、dp数组如何初始化
关于初始化,一定要和dp数组的定义吻合。
如果背包容量j
为0的话,dp[i][0]
无论是选取哪些物品,背包价值总和一定为0。
由递推可知i是由i-1推出来的,那么i为0时一定要初始化。
dp[0][j]
存放编号为0的物品时,各个容量的背包能存放的最大价值:
for(int j = bagWeight; j >= weight[0]; j--)
{dp[0][j] = dp[0][j-weight[0]] + value[0];
}
这里需要注意,初始化是倒序遍历。
dp[0][j]
表示容量为j的背包存放物品0时候的最大价值。由于每个物品只有1个,如果dp[0][j]
必须为初值,正序遍历,物品0会被重复加入多次。
dp[i][j]
在推导的时候一定是取价值最大的数,如果题目给的价值都是正整数,那么下标初始化为0.如果价值里面有负数,初始化为负无穷。只要保证dp数组在递推公式的过程中取最大的价值,而不是被初始值覆盖。
所以dp数组初始化如下:
vector<vector<int>> dp(weight.size() + 1,vector<int>(bagWeight + 1,0));
for(int j = bagWeight; j >= weight[0]; j--)
{dp[0][j] = dp[0][j-weight[0]] + value[0];
}
4、确定遍历顺序
有两个遍历维度:物品与背包重量,先遍历物品更好理解。
for(int i = 1; i < weight.size(); i++) //遍历物品
{for(int j = 0; j <= bagWeight; j++) //遍历背包容量 { if(j < weight[i]) dp[i][j] = dp[i-1][j]; //else dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+values[i])}
}
滚动数组优化
1、确定dp数组的定义
dp[j]:容量为j的背包,所背的物品价值可以最大为dp[j]。
2、一维dp递推公式
dp[j]可以通过dp[j-weight[i]]推导,其表示容量为j-weight[i]的背包所背的最大价值。
dp[j-weight[i]]+value[i]表示容量为j-物品i重量的背包加上物品i的价值。(即容量为j的背包放入物品i之后的价值)此时dp[j]有两个选择,一个是取自己dp[j],一个是取dp[j-weight[i]]+value[i].
所以递推公式为:
dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
3、初始化
假设物品价值都是大于0的,dp数组初始化的时候都初始化为0
4、确定遍历顺序
for(int i = 0; i < weight.size(); i++) //遍历物品
{for(int j = bagWeight; j >= weight[i]; j--) //遍历背包容量{dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);}
}
二维遍历时,背包容量从小到大,一维遍历,背包容量从大到小。
这是因为倒序遍历是为了保证物品i只被放入一次。二维dp,dp[i][j]
是通过上一层dp[i-1][j]
计算得到的,所以本层的dp[i][j]
并不会产生覆盖。
一维01背包测试代码:
void test_one_dim_01bag()
{vector<int> weight = {1,3,4};vector<int> value = {15,20,30};int bagWeight = 4;//初始化vector<int> dp(bagWeight+1,0);for(int i = 0; i < weight[i]; i++) //遍历物品{for(int j = bagWeight; j >= weight[i]; j--) //遍历背包容量{dp[j] = max(dp[j],dp[j-weight[i]] + value[i]);}}cout << dp[bagWeight] << endl;
}
416. 分割等和子集
dp[j]表示容量为j的背包,所背物品价值可以最大为dp[j]。
dp[j]表示背包总容量为j,最大可以凑成j的子集总和为dp[j]。
dp[j] = max(dp[j],dp[j-nums[i]] + nums[i]);
vector<int> dp(target+1,0); //target为背包容量
for(int i = 0; i < nums.size(); i++)
{for(int j = target; j >= nums[i]; j--){dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);}
}
class Solution {
public:bool canPartition(vector<int>& nums) {int sum = 0;for(int num : nums){sum += num;}if(sum % 2 != 0) return false;int target = sum / 2;vector<int> dp(target+1,0);for(int i = 0; i < nums.size(); i++){for(int j = target; j >= nums[i]; j--){dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);if(dp[j] == target) return true;}}if(dp[target] == target) return true;else return false;}
};
1049. 最后一块石头的重量 II
将石头尽量分成两堆相同重量,然后相撞。分成两堆的思路与上一题一致。
class Solution {
public:int lastStoneWeightII(vector<int>& stones) {int sum = 0;for(int stone : stones){sum += stone;}int target = sum / 2;//dp[target],容量为target的背包最多能背多重的石头vector<int> dp(target+1,0);for(int i = 0; i < stones.size(); i++){for(int j = target; j >= stones[i]; j--){dp[j] = max(dp[j],dp[j-stones[i]] + stones[i]);}}return sum - dp[target]*2;}
};
494. 目标和
dp[j] += dp[j-nums[i]]
3、初始化dp数组
dp[0] = 1,装满容量为0的背包,有1种方法。其他dp[i]均设置为0,在不知道nums[i]的情况下,没有方法。
class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = 0;for(int num : nums)sum += num;//如果绝对值和比targetabs小,说明都用同一个符号也不能凑成if(sum < abs(target)) return 0;//如果不能完整的分成两组,那么说明没有方法if((target + sum) % 2 == 1) return 0;int bagWeight = (target + sum)/2;vector<int> dp(bagWeight+1,0);dp[0] = 1;for(int i = 0; i < nums.size(); i++){for(int j = bagWeight; j >= nums[i]; j--){dp[j] += dp[j-nums[i]];}}return dp[bagWeight];}
};
《dp补卡——01背包问题》相关推荐
- ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- 信息学奥赛真题解析(玩具谜题)
玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...
- 信息学奥赛之初赛 第1轮 讲解(01-08课)
信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...
- 信息学奥赛一本通习题答案(五)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通习题答案(三)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题
第1章 快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章 素数 第 3 章 约数 第 4 章 同余问题 第 5 章 矩阵乘法 第 6 章 ...
- 信息学奥赛一本通题目代码(非题库)
为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...
- 信息学奥赛一本通(C++版) 刷题 记录
总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...
- 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离
首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...
最新文章
- 全局唯一ID生成方案
- UIWebView保存网页中的图片(转载)
- Django Logging
- PHP实现图片马赛克效果
- 腾讯微博发表带图片的微博
- msbuild explorer
- 从源码的角度再看 React JS 中的 setState
- spring boot----简单入门
- 增量式pid_PID 基础知识汇总
- 马化腾生日当天 微信支付居然崩溃了//(ㄒoㄒ)//
- P3667 [USACO17OPEN]Bovine Genomics
- MATLAB关于Mesh的相关命令
- Java生产者 消费者模型的一种实现
- python求字符组合_python – 生成一个潜在的8个字符串的所有可能的2个字符组合?...
- 设置电脑的双显示器模式
- ps使用教程 核心蒙版
- MBR、主分区、扩展分区、逻辑分区、活动分区、系统分区、启动分区讲解
- 输入两个正整数m和n,求其最大公约数和最小公倍数(常见的错误代码分析)
- 《计算机网络 第7版》第9章 无线局域网的物理层和MAC层
- 使用Java导出Excel表格并由浏览器直接下载——基于POI框架
热门文章
- mysql 存储过程 记录是否存在_如何检查MySQL中是否存在存储过程?
- python 编辑数学公式_用python编写数学公式
- java绘制_Java 绘制简单图形的问题
- [one day one question] safari缓存太厉害
- 复习上学期的HTML CSS(1)
- ife 零基础学院 day 2
- 从一个帖子看 所谓“知乎” 的真实水平
- cd1101d 树形dp
- 使用MyBatis框架时发现的一些小bug
- html中padding和margin的区别和用法与存在的bug消除