01背包问题-递推公式的自我理解与LeetCode 416. 分割等和子集

这篇具有很好参考价值的文章主要介绍了01背包问题-递推公式的自我理解与LeetCode 416. 分割等和子集。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学算法好痛苦,完全是对我智力的一次次折磨,看了好多博客,对二维dp数组的理解都是直接搬了代码随想录,搬了随想录又没详细解释,大家都是一眼看懂的吗,好吧()

一、对二维dp数组的理解

背景

  1. 如果有一个容量为j的这样的背包——一个独立的,容量为j的背包。(没把它理解为“剩余容量”)
  2. 对于这个背包,可以从编号为0至i的物品里任意挑,挑几个无所谓,挑多重的无所谓
  3. 计算第二步挑选的价值,取所有挑选中使整个背包价值最大的这次挑选,将最大的价值记为dp[i][j]
  4. 根据前三步,整体的dp[n][m]就是一个n+1行、m+1列的表格。取其中的一格看,比如第i行第j列的dp[i][j],它的含义为:对于容量为j的背包,编号0-i的物品随便挑,背包最大价值为多少。下面,我们详细说一下dp[i][j]的递推公式。

dp[i][j]的推导

  1. dp[i][j]的推导看起来很难,因为要对i+1个物品每个物品0~i都选择放或者不放,那么就 2 i + 1 2^{i+1} 2i+1次挑选
  2. 但是实际情况下,求dp[i][j]只需要考虑第i个物品的放与不放,也就是对于这个格子只要考虑对于1个物品的2次挑选方式(选与不选)+1次查表(查表——我们遍历表格dp[n][m]的时候已经填写了在dp[i][j]之前的格子,之前的最优挑选查找表格即可。)
  3. 为什么可以通过1次查表简化前 2 i 2^i 2i次挑选?
  • 首先,不考虑背包容量,如果我们需要dp[i][j]是价值最大的,那么可以分两种情况:在第三步选定“这次挑选”的时候,dp[i][j]【情况1:使整个背包价值最大的这次挑选含第i个物品】,【情况2:使整个背包价值最大的这次挑选不含第i个物品】,那么合起来
dp[i][j]=max{含第i个物品的挑选的背包价值,不含第i个物品的挑选的背包价值}
  • 其次,再考虑上背包容量,
    ①含第i个物品的挑选的背包价值:由于第i个物品占一定的容量,此时需要调整第0到第i-1个物品的挑选方式。因为需要预留第i个物品的容量,所以对于第0到第i-1个物品,最多只能j-weight[i]的容量,价值为查表得到的dp[i-1][j-weight[i]];对于第i个物品,确定是放,所以再加上它本身的价值value[i]。
    ②不含第i个物品的挑选的背包价值:与dp[i-1][j]相同。因为这种情况等于是从容量为j的背包里选,但只从第0到第i-1个物品里挑。
dp[i][j]
=max{含第i个物品的挑选的背包价值,同样大小的背包不含第i个物品的挑选}
=max{dp[i-1][j],dp[i-1][j-weight[i]]+value[i]}

01背包问题-递推公式的自我理解与LeetCode 416. 分割等和子集
备注:①中还需要判断 j 是否小于 weight[i],不然j-weight[i]就变成负值,无法成为数组下标。

二、优化:滚动一维数组

如不考虑中间状态,可优化空间复杂度,仅使用j+1个空间,而非(i+1)*(j+1)个。
01背包问题-递推公式的自我理解与LeetCode 416. 分割等和子集
需要逆序(遍历 j 时),以保证从后往前的时候前面的初始化是没动的,不然dp[j-weight[i]]的计算会用到前面已经加过value[i]的元素,从而再加一个。这个看代码随想录手动推一遍还是比较简单,故不细说。

例1 LeetCode 416. 分割等和子集

由于看了之前的0-1背包,被困住了思路,老是觉得0-1背包是求的一堆元素的“最大值”,而等和子集求的是一堆元素等于某个值的情况,思想上感觉不一样。知道回溯法不配合剪枝肯定不行(200个num, 2 200 2^{200} 2200
然后隔了一天看了别人没学代码随想录的题解,发现其实很简单,因为我误认为我这里的dp[][]里存放的是和,或者一个特定的值什么的,其实不是,就是单纯的布尔变量,1,或者0。dp[i][j]代表:i个元素,任意选择是否"可"为j?可->1,不可->0,就这么简单。

class Solution {
public:
    bool canPartition(vector<int>& nums) 
    {
           //元素和相等——>数字集合的和等于1/2 sum
        if (accumulate(nums.begin(), nums.end(), 0) % 2 != 0 || nums.size() == 1)
            return 0;

        int target = accumulate(nums.begin(), nums.end(), 0) / 2;
        vector <vector<int>> dp(nums.size(), vector<int>(target + 1,0));
        //vector<vector<int>> vec(row, vector<int> (col,0)); -- 用 0 来初始化二维vector

        for (int i = 0; i < nums.size(); ++i) {
            dp[i][0] = 1;//均有方法可使得和为0
        }

        for (int j = 1; j <= target; ++j) {
            dp[0][j] = (j == nums[0]);
        }
        for (int i = 1; i < nums.size(); ++i)
        {
            for (int j = 1; j <= target; ++j)
            {
                dp[i][j] = dp[i - 1][j];
                if (j - nums[i] >= 0)
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
            }
            if (dp[i][target] == 1) 
                return 1;
        }
        return 0;
    }
};

notes

第一,vector二维数组的初始化问题。如果不手动初始化的话,似乎vector只会初始化一行,所以需要使用vector<vector<int>> vec(row, vector<int> (col,0)); -- 用 0 来初始化二维vector这一行代码,前面是row的数量,后面是col的数量。
第二,dp的初始化。第一次写的时候,初始化很想当然,理清思路以后初始化还是比较简单的。文章来源地址https://www.toymoban.com/news/detail-470641.html

到了这里,关于01背包问题-递推公式的自我理解与LeetCode 416. 分割等和子集的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Day42|动态规划part04: 01背包问题,你该了解这些!、滚动数组、416. 分割等和子集

    其他背包,面试几乎不会问,都是竞赛级别的了,leetcode上连多重背包的题目都没有,所以题库也告诉我们,01背包和完全背包就够用了。 而完全背包又是也是01背包稍作变化而来,即:完全背包的物品数量是无限的。 01 背包问题描述 有n件物品和一个最多能背重量为w 的背包

    2024年04月25日
    浏览(38)
  • leetcode刷题之背包问题(01背包)

    01 背包 概念:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是 w e i g h t [ i ] weight[i] w e i g h t [ i ] ,得到的价值是 v a l u e [ i ] value[i] v a l u e [ i ] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 方法1:暴力回溯法 方法2:动态规划 三个

    2024年02月02日
    浏览(50)
  • 【第二十五课】动态规划:完全背包问题(acwing-3 / 公式推导 / 思路理解 / 优化 / c++代码)

    目录 思路 朴素代码 优化 公式推导上  二维代码  一维代码 公式理解上   在开始看完全背包问题之前,可能需要先了解01背包及其解决办法。 指路👇 【第二十五课】动态规划:01背包问题(acwing-2 / 思路 / 含一维数组优化 / c++代码) 这个问题和01背包的区别就是 每件物品可以

    2024年03月19日
    浏览(64)
  • 【十七】【动态规划】DP41 【模板】01背包、416. 分割等和子集、494. 目标和,三道题目深度解析

    动态规划就像是解决问题的一种策略,它可以帮助我们更高效地找到问题的解决方案。这个策略的核心思想就是将问题分解为一系列的小问题,并将每个小问题的解保存起来。这样,当我们需要解决原始问题的时候,我们就可以直接利用已经计算好的小问题的解,而不需要重

    2024年02月03日
    浏览(45)
  • 【LeetCode动态规划#06】分割等和子集(01背包问题一维写法实战)

    分割等和子集 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。 示例 1: 输入:nums = [1,5,11,5] 输出:true 解释:数组可以分割成 [1, 5, 5] 和 [11] 示例 2: 输入:nums = [1,2,3,5] 输出:false 解释:数组不能分割

    2023年04月09日
    浏览(67)
  • 【LeetCode动态规划#07】01背包问题一维写法(状态压缩)实战,其二(目标和、零一和)

    力扣题目链接(opens new window) 难度:中等 给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。 返回可以使最终数组和为目标数 S 的所有添加符号的方法数。 示例: 输入

    2023年04月18日
    浏览(63)
  • 代码随想录day42|背包问题、416. 分割等和子集

     背包问题:    01 背包 二维数组dp[i][j]解法 纯01背包:有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。 每件物品只能用一次 ,求解将哪些物品装入背包里物品价值总和最大。 dp[i][j]:从下标为[0-i]的物品里任意取,放进容量为j的

    2024年04月09日
    浏览(62)
  • 背包问题——01背包|完全背包

    目录 前言背包问题的历史  01背包  1、题目 2、暴力解01背包  Ⅰ、代码 3、动态规划解01背包 Ⅰ、二维dp数组解01背包 1)dp数组的含义  2)递推公式  3)dp数组的初始化  4)遍历顺序的讨论  5、代码  Ⅱ、一维数组解01背包  1)一维数组|滚动数组  2)一维数组的含义及递

    2024年02月02日
    浏览(48)
  • 背包问题(贪心)& 二维01背包问题 Java

    题目描述 有n件物品和一个最大承重为w 的背包。第i件物品的重量是weight[i],每件只能用一次,求装入背包的最多物品数量。 题目分析 因为我们 只要求装入物品的数量 ,所以 装重的显然没有装轻的划算 。 因此将 数组weight[i]按从小到大排序 , 依次选择每个物品 ,直到装不

    2024年01月17日
    浏览(42)
  • 背包问题分析代码详解【01背包+完全背包+多重背包】

    一、01背包问题 问题描述: 有 N 件物品和一个容量为 V 的背包,每件物品有各自的价值且只能被选择一次,要求在有限的背包容量下,装入的物品总价值最大。 朴素01背包 状态f[i , j]定义:在前i个物品中选,总体积不超过j的价值最大值 状态转移 1) 选第i个物品:f[i,j] = f

    2024年02月06日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包