当前位置: 首页 > news >正文

北京网站建设公司分形科技微信广告推广价格表

北京网站建设公司分形科技,微信广告推广价格表,企业网站建设实训体会,杭州江干区抖音seo哪里有动态规划part0401背包问题 二维01 背包二维dp数组01背包完整c测试代码总结01背包问题 一维一维dp数组#xff08;滚动数组#xff09;一维dp01背包完整C测试代码416. 分割等和子集题目描述思路01背包问题总结01背包问题 二维 视频链接#xff1a;https://www.bilibili.com/… 动态规划part0401背包问题 二维01 背包二维dp数组01背包完整c测试代码总结01背包问题 一维一维dp数组滚动数组一维dp01背包完整C测试代码416. 分割等和子集题目描述思路01背包问题总结01背包问题 二维 视频链接https://www.bilibili.com/video/BV1cg411g7Y6/ 参考https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-1.html 对于面试的话其实掌握01背包和完全背包就够用了最多可以再来一个多重背包。 而完全背包又是也是01背包稍作变化而来即完全背包的物品数量是无限的。 所以背包问题的理论基础重中之重是01背包一定要理解透 leetcode上没有纯01背包的问题都是01背包应用方面的题目也就是需要转化为01背包问题。 所以我先通过纯01背包问题把01背包原理讲清楚后续再讲解leetcode题目的时候重点就是讲解如何转化为01背包问题了。 01 背包 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]得到的价值是value[i] 。每件物品只能用一次求解将哪些物品装入背包里物品价值总和最大。 这是标准的背包问题以至于很多同学看了这个自然就会想到背包甚至都不知道暴力的解法应该怎么解了。 这样其实是没有从底向上去思考而是习惯性想到了背包那么暴力的解法应该是怎么样的呢 每一件物品其实只有两个状态取或者不取所以可以使用回溯法搜索出所有的情况那么时间复杂度就是o(2n)o(2^n)o(2n)这里的n表示物品数量。 所以暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化 在下面的讲解中我举一个例子 背包最大重量为4。 物品为 问背包能背的物品最大价值是多少 以下讲解和图示中出现的数字都是以这个例子为例。 二维dp数组01背包 依然动规五部曲分析一波。 确定dp数组以及下标的含义 对于背包问题有一种写法 是使用二维数组即dp[i][j] 表示从下标为[0-i]的物品里任意取放进容量为j的背包价值总和最大是多少。 只看这个二维数组的定义大家一定会有点懵看下面这个图 要时刻记着这个dp数组的含义下面的一些步骤都围绕这dp数组的含义进行的如果哪里看懵了就来回顾一下i代表什么j又代表什么。 确定递推公式 再回顾一下dp[i][j]的含义从下标为[0-i]的物品里任意取放进容量为j的背包价值总和最大是多少。 那么可以有两个方向推出来dp[i][j] 不放物品i由dp[i - 1][j]推出即背包容量为j里面不放物品i的最大价值此时dp[i][j]就是dp[i - 1][j]。(其实就是当物品i的重量大于背包j的重量时物品i无法放进背包中所以被背包内的价值依然和前面相同。)放物品i由dp[i - 1][j - weight[i]]推出dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值那么dp[i - 1][j - weight[i]] value[i] 物品i的价值就是背包放物品i得到的最大价值 所以递归公式 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); dp数组如何初始化 关于初始化一定要和dp数组的定义吻合否则到递推公式的时候就会越来越乱。 首先从dp[i][j]的定义出发如果背包容量j为0的话即dp[i][0]无论是选取哪些物品背包价值总和一定为0。如图 在看其他情况。 状态转移方程 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); 可以看出i 是由 i-1 推导出来那么i为0的时候就一定要初始化。 dp[0][j]即i为0存放编号0的物品的时候各个容量的背包所能存放的最大价值。 那么很明显当 j weight[0]的时候dp[0][j] 应该是 0因为背包容量比编号0的物品重量还小。 当j weight[0]时dp[0][j] 应该是value[0]因为背包容量放足够放编号0物品。 代码初始化如下 for (int j 0 ; j weight[0]; j) { // 当然这一步如果把dp数组预先初始化为0了这一步就可以省略但很多同学应该没有想清楚这一点。dp[0][j] 0; } // 正序遍历 for (int j weight[0]; j bagweight; j) {dp[0][j] value[0]; }此时dp数组初始化情况如图所示 dp[0][j] 和 dp[i][0] 都已经初始化了那么其他下标应该初始化多少呢 其实从递归公式 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); 可以看出dp[i][j] 是由左上方数值推导出来了那么 其他下标初始为什么数值都可以因为都会被覆盖。 初始-1初始-2初始100都可以 但只不过一开始就统一把dp数组统一初始为0更方便一些。 如图 最后初始化代码如下 // 初始化 dp vectorvectorint dp(weight.size(), vectorint(bagweight 1, 0)); for (int j weight[0]; j bagweight; j) {dp[0][j] value[0]; } 确定遍历顺序 在如下图中可以看出有两个遍历的维度物品与背包重量 那么问题来了先遍历 物品还是先遍历背包重量呢 其实都可以 但是先遍历物品更好理解。 那么我先给出先遍历物品然后遍历背包重量的代码。 // weight数组的大小 就是物品个数 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]] value[i]);} }先遍历背包再遍历物品也是可以的注意我这里使用的二维dp数组 例如这样 // weight数组的大小 就是物品个数 for(int j 0; j bagweight; j) { // 遍历背包容量for(int i 1; i weight.size(); i) { // 遍历物品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]] value[i]);} }为什么也是可以的呢 要理解递归的本质和递推的方向。 dp[i][j] max(dp[i - 1][j], dp[i - 1][j - weight[i]] value[i]); 递归公式中可以看出dp[i][j]是靠dp[i-1][j]和dp[i - 1][j - weight[i]]推导出来的。 dp[i-1][j]和dp[i - 1][j - weight[i]] 都在dp[i][j]的左上角方向包括正上方向那么先遍历物品再遍历背包的过程如图所示 再来看看先遍历背包再遍历物品呢如图 大家可以看出虽然两个for循环遍历的次序不同但是dp[i][j]所需要的数据就是左上角根本不影响dp[i][j]公式的推导 但先遍历物品再遍历背包这个顺序更好理解。 其实背包问题里两个for循环的先后循序是非常有讲究的理解遍历顺序其实比理解推导公式难多了。 举例推导dp数组 来看一下对应的dp数组的数值如图 最终结果就是dp[2][4]。 建议大家此时自己在纸上推导一遍看看dp数组里每一个数值是不是这样的。 做动态规划的题目最好的过程就是自己在纸上举一个例子把对应的dp数组的数值推导一下然后在动手写代码 很多同学做dp题目遇到各种问题然后凭感觉东改改西改改怎么改都不对或者稀里糊涂就改过了。 主要就是自己没有动手推导一下dp数组的演变过程如果推导明白了代码写出来就算有问题只要把dp数组打印出来对比一下和自己推导的有什么差异很快就可以发现问题了。 完整c测试代码 void test_2_wei_bag_problem1() {vectorint weight {1, 3, 4};vectorint value {15, 20, 30};int bagweight 4;// 二维数组vectorvectorint dp(weight.size(), vectorint(bagweight 1, 0));// 初始化for (int j weight[0]; j bagweight; j) {dp[0][j] value[0];}// weight数组的大小 就是物品个数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]] value[i]);}}cout dp[weight.size() - 1][bagweight] endl; }int main() {test_2_wei_bag_problem1(); } 总结 讲了这么多才刚刚把二维dp的01背包讲完这里大家其实可以发现最简单的是推导公式了推导公式估计看一遍就记下来了但难就难在如何初始化和遍历顺序上。 01背包问题 一维 视频链接https://www.bilibili.com/video/BV1BU4y177kY 参考https://programmercarl.com/%E8%83%8C%E5%8C%85%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%8001%E8%83%8C%E5%8C%85-2.html 那么我们通过01背包来彻底讲一讲滚动数组 接下来还是用如下这个例子来进行讲解 背包最大重量为4。 物品为 问背包能背的物品最大价值是多少 一维dp数组滚动数组 对于背包问题其实状态都是可以压缩的。 在使用二维数组的时候递推公式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[i - 1]这一层拷贝到dp[i]上不如只用一个一维数组了只用dp[j]一维数组也可以理解是一个滚动数组。 这就是滚动数组的由来需要满足的条件是上一层可以重复利用直接拷贝到当前层。 读到这里估计大家都忘了 dp[i][j]里的i和j表达的是什么了i是物品j是背包容量。 dp[i][j] 表示从下标为[0-i]的物品里任意取放进容量为j的背包价值总和最大是多少。 一定要时刻记住这里i和j的含义要不然很容易看懵了。 动规五部曲分析如下 确定dp数组的定义 在一维dp数组中dp[j]表示容量为j的背包所背的物品价值可以最大为dp[j]。 一维dp数组的递推公式 dp[j]为 容量为j的背包所背的最大价值那么如何推导dp[j]呢 dp[j]可以通过dp[j - weight[i]]推导出来dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。 dp[j - weight[i]] value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。也就是容量为j的背包放入物品i了之后的价值即dp[j] 此时dp[j]有两个选择一个是取自己dp[j] 相当于 二维dp数组中的dp[i-1][j]即不放物品i一个是取dp[j - weight[i]] value[i]即放物品i指定是取最大的毕竟是求最大价值 所以递归公式为 dp[j] max(dp[j], dp[j - weight[i]] value[i]);可以看出相对于二维dp数组的写法就是把dp[i][j]中i的维度去掉了。 一维dp数组如何初始化 关于初始化一定要和dp数组的定义吻合否则到递推公式的时候就会越来越乱。 dp[j]表示容量为j的背包所背的物品价值可以最大为dp[j]那么dp[0]就应该是0因为背包容量为0所背的物品的最大价值就是0。 那么dp数组除了下标0的位置初始为0其他下标应该初始化多少呢 看一下递归公式dp[j] max(dp[j], dp[j - weight[i]] value[i]); dp数组在推导的时候一定是取价值最大的数如果题目给的价值都是正整数那么非0下标都初始化为0就可以了。 这样才能让dp数组在递归公式的过程中取的最大的价值而不是被初始值覆盖了。 那么我假设物品价值都是大于0的所以dp数组初始化的时候都初始为0就可以了。 一维dp数组遍历顺序 代码如下 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]);} }这里大家发现和二维dp的写法中遍历背包的顺序是不一样的 二维dp遍历的时候背包容量是从小到大而一维dp遍历的时候背包是从大到小。 为什么呢 倒序遍历是为了保证物品i只被放入一次。但如果一旦正序遍历了那么物品0就会被重复加入多次 举一个例子物品0的重量weight[0] 1价值value[0] 15 如果正序遍历 dp[1] dp[1 - weight[0]] value[0] 15 dp[2] dp[2 - weight[0]] value[0] 30 此时dp[2]就已经是30了意味着物品0被放入了两次所以不能正序遍历。 为什么倒序遍历就可以保证物品只放入一次呢 倒序就是先算dp[2] dp[2] dp[2 - weight[0]] value[0] 15 dp数组已经都初始化为0 dp[1] dp[1 - weight[0]] value[0] 15 所以从后往前循环每次取得状态不会和之前取得状态重合这样每种物品就只取一次了。 那么问题又来了为什么二维dp数组历的时候不用倒序呢 因为对于二维dpdp[i][j]都是通过上一层即dp[i - 1][j]计算而来本层的dp[i][j]并不会被覆盖 如何这里读不懂大家就要动手试一试了空想还是不靠谱的实践出真知 再来看看两个嵌套for循环的顺序代码中是先遍历物品嵌套遍历背包容量那可不可以先遍历背包容量嵌套遍历物品呢 不可以 因为一维dp的写法背包容量一定是要倒序遍历原因上面已经讲了如果遍历背包容量放在上一层那么每个dp[j]就只会放入一个物品即背包里只放入了一个物品。 倒序遍历的原因是本质上还是一个对二维数组的遍历并且右下角的值依赖上一层左上角的值因此需要保证左边的值仍然是上一层的从右向左覆盖。 这里如果读不懂就再回想一下dp[j]的定义或者就把两个for循环顺序颠倒一下试试 所以一维dp数组的背包在遍历顺序上和二维其实是有很大差异的这一点大家一定要注意。 举例推导dp数组 一维dp分别用物品0物品1物品2 来遍历背包最终得到结果如下 一维dp01背包完整C测试代码 void test_1_wei_bag_problem() {vectorint weight {1, 3, 4};vectorint value {15, 20, 30};int bagWeight 4;// 初始化vectorint dp(bagWeight 1, 0);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]);}}cout dp[bagWeight] endl; }int main() {test_1_wei_bag_problem(); }可以看出一维dp 的01背包要比二维简洁的多 初始化 和 遍历顺序相对简单了。 所以我倾向于使用一维dp数组的写法比较直观简洁而且空间复杂度还降了一个数量级 416. 分割等和子集 题目链接416. 分割等和子集 参考https://programmercarl.com/0416.%E5%88%86%E5%89%B2%E7%AD%89%E5%92%8C%E5%AD%90%E9%9B%86.html 题目描述 题目难易中等 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集使得两个子集的元素和相等。 注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200 示例 1: 输入: [1, 5, 11, 5]输出: true解释: 数组可以分割成 [1, 5, 5] 和 [11]. 示例 2: 输入: [1, 2, 3, 5]输出: false解释: 数组不能分割成两个元素和相等的子集. 提示 1 nums.length 2001 nums[i] 100 思路 这道题目初步看和如下两题几乎是一样的大家可以用回溯法解决如下两题 698.划分为k个相等的子集 473.火柴拼正方形 这道题目是要找是否可以将这个数组分割成两个子集使得两个子集的元素和相等。 那么只要找到集合里能够出现 sum / 2 的子集总和就算是可以分割成两个相同元素和子集了。 本题是可以用回溯暴力搜索出所有答案的但最后超时了也不想再优化了放弃回溯直接上01背包吧。 01背包问题 背包问题大家都知道有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i]得到的价值是value[i] 。每件物品只能用一次求解将哪些物品装入背包里物品价值总和最大。 背包问题有多种背包方式常见的有01背包、完全背包、多重背包、分组背包和混合背包等等。 要注意题目描述中商品是不是可以重复放入。 即**一个商品如果可以重复多次放入是完全背包而只能放入一次是01背包**写法还是不一样的。 要明确本题中我们要使用的是01背包因为元素我们只能用一次。 回归主题首先本题要求集合里能否出现总和为 sum / 2 的子集。 那么来一一对应一下本题看看背包问题如何来解决。 只有确定了如下四点才能把01背包问题套到本题上来。 背包的体积为sum / 2背包要放入的商品集合里的元素重量为 元素的数值价值也为元素的数值背包如果正好装满说明找到了总和为 sum / 2 的子集。背包中每一个元素是不可重复放入。 以上分析完我们就可以套用01背包来解决这个问题了。 动规五部曲分析如下 确定dp数组以及下标的含义 01背包中dp[j] 表示 容量为j的背包所背的物品价值最大可以为dp[j]。 本题中每一个元素的数值既是重量也是价值。 套到本题dp[j]表示 背包总容量所能装的总重量是j放进物品后背的最大重量为dp[j]。 那么如果背包容量为target dp[target]就是装满 背包之后的重量所以 当 dp[target] target 的时候背包就装满了。 那还有装不满的时候 拿输入数组 [1, 5, 11, 5]举例 dp[7] 只能等于 6因为 只能放进 1 和 5。 而dp[6] 就可以等于6了放进1 和 5那么dp[6] 6说明背包装满了。 确定递推公式 01背包的递推公式为dp[j] max(dp[j], dp[j - weight[i]] value[i]); 本题相当于背包里放入数值那么物品i的重量是nums[i]其价值也是nums[i]。 所以递推公式dp[j] max(dp[j], dp[j - nums[i]] nums[i]); dp数组如何初始化 在01背包一维dp如何初始化已经讲过 从dp[j]的定义来看首先dp[0]一定是0。 如果题目给的价值都是正整数那么非0下标都初始化为0就可以了如果题目给的价值有负数那么非0下标就要初始化为负无穷。 这样才能让dp数组在递推的过程中取得最大的价值而不是被初始值覆盖了。 本题题目中 只包含正整数的非空数组所以非0下标的元素初始化为0就可以了。 代码如下 // 题目中说每个数组中的元素不会超过 100数组的大小不会超过 200 // 总和不会大于20000背包最大只需要其中一半所以10001大小就可以了 vectorint dp(10001, 0);确定遍历顺序 前面已经说过如果使用一维dp数组物品遍历的for循环放在外层遍历背包的for循环放在内层且内层for循环倒序遍历 代码如下 // 开始 01背包 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]);} }举例推导dp数组 dp[j]的数值一定是小于等于j的。 如果dp[j] j 说明集合中的子集总和正好可以凑成总和j理解这一点很重要。 用例1输入[1,5,11,5] 为例如图 最后dp[11] 11说明可以将这个数组分割成两个子集使得两个子集的元素和相等。 综上分析完毕C代码如下 class Solution { public:bool canPartition(vectorint nums) {int sum 0;// dp[i]中的i表示背包内总和// 题目中说每个数组中的元素不会超过 100数组的大小不会超过 200// 总和不会大于20000背包最大只需要其中一半所以10001大小就可以了vectorint dp(10001, 0);for (int i 0; i nums.size(); i) {sum nums[i];}// 也可以使用库函数一步求和// int sum accumulate(nums.begin(), nums.end(), 0);if (sum % 2 1) return false;int target sum / 2;// 开始 01背包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]);}}// 集合中的元素正好可以凑成总和targetif (dp[target] target) return true;return false;} };时间复杂度O(n^2)空间复杂度O(n)虽然dp数组大小为一个常数但是大常数 总结 这道题目就是一道01背包应用类的题目需要我们拆解题目然后套入01背包的场景。 01背包相对于本题主要要理解题目中物品是nums[i]重量是nums[i]价值也是nums[i]背包体积是sum/2。 看代码的话就可以发现基本就是按照01背包的写法来的。
http://www.tj-hxxt.cn/news/225687.html

相关文章:

  • asp.net网站登录wordpress主题点赞
  • 北京网站优化软件网站 流程 工具
  • ftp免费网站空间百度网站建设教程
  • 网站突然打不开的原因是wordpress配置邮件服务器
  • 如何做logo模板下载网站广州万户网络技术有限公司深圳分公司
  • 网站创建费用商务网站建设需要多少钱
  • 网站系统的建设与管理工作服定制无锡帛裳 服饰实力
  • 网站好友邀请链接生成 php做网站维护有前途吗
  • 做网站会遇到的问题有没有电脑做兼职的网站
  • 企业网站建设湖南岚鸿易企秀h5页面怎么制作
  • 东莞网站营销公司美容类 营销型网站
  • 青岛开发区网站建设哪家好字体中国设计网
  • 网站首页代码红安建设局投诉网站
  • 教做家常菜的视频网站淄博网站快照优化公司
  • 成都网页设计与网站建设成都企业品牌网站建设
  • 校园网站建设促进教学沧州营销软件
  • 最贵网站建设网页制作的目的和意义
  • 做美食介绍的网站员工管理网站模板
  • 网站对图片优化门户网站建站多少钱
  • 重庆专业建网站wordpress修改 id
  • 网站 注册模块怎么做正规网站备案信息表
  • 扬州外贸网站建设wordpress 古风主题
  • 网站建设论文答辩自述网站开发需要兼容到ie几
  • 展厅设计制作网站如何是网站排名上升
  • 化州网站建设公司网站推广策划书目录
  • 怎么查网站是谁建的30天网站建设实录 pdf
  • 网站建设主动型电话销售话术一个网站两个空间
  • 门户网站系统有哪些平台玉溪做网站
  • 海外营销网站建设中天建设集团有限公司第九建设公司
  • 如何攻克房地产网站西地那非最佳吃法