草庐IT

01背包面试题系列(一)

Chang-LeHung 2023-03-28 原文

01背包面试题系列(一)

题目描述——分割等和子集

给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

01背包动态规划求解

上面的问题乍一看好像是一个子集划分的问题,我们可能根据数据nums得到它的所有的子集,然后将所有的自己加起来看看是否存在一个子集的数据的和等于nums数组所有数据的和的一半,其实我们确实可以这样做,我们将在后文当中仔细探讨这个方法。

那么我们改如何使用01背包去解决这个问题呢?我们首先先回顾一下01背包解决了什么问题:

\(N\)件物品和一个容量是 \(V\) 的背包。每件物品只能使用一次。第\(i\)件物品的体积是\(v_i\),价值是 \(w_i\)。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。

01背包就是给定一定容量的背包,看他能够装入物品的最大的价值。那么我们该如何将上述问题转化成01背包呢?

我们可以这样,将nums数组当中的数值变成物品对应的价值和体积,比如说nums = [1, 2, 3],我们就可以分成三个物品,这三个物品的体积和价值分别是1和12和23和3,我们背包的容量为V = (1 + 2 + 3) / 2。我们将nums数组当中所有数据和的一半作为背包的容量,nums当中的数值就表示每一个物品的价值和体积,而且价值和体积都相等,如果我们能够恰好装满背包就说明,存在一种物品的组合他的和为nums数组当中数据的和的一半。

那么我们该如何判断背包被恰好装满呢?我们知道背包问题求解的只是在背包容量一定的情况下,我们能够获取的最大的价值是多少,因此好像不能够判断背包是否装满!但是在上面转化的过程当中物品的体积和价值是相等的,因为我们可以根据我们获得的最大价值判断,如果我们最终得到的收益等于背包的容量,那么说明背包被填满了,也就是存在一种物品的组合我们能够获取的最大的价值等于数组当中数据和的一半。因为物品的价值和体积相等,因此把背包填满和获取最大价值是等价的。

因此根据上面的分析,如果我们想用01背包去解决这个问题,我们可以将背包容量设置为nums数组当中数据和的一半,物品的个数为数组的长度,物品的价值和体积为数组当中对应位置的值,然后进行01背包求解即可。

如果你还不是很了解01背包的话,可以先看这篇文章,该文章主要从0开始介绍了01背包问题,从二维数组到滚动数组再到一维数组,优化过程层层递进,带你从原理到实战完全掌握01背包问题。

因此我们的代码如下(一维数组优化):

class Solution {
  public boolean canPartition(int[] nums) {
    int sum = 0;
    for (int num : nums) {
      sum += num;
    }
    if ((sum & 1) == 1) return false;
    int t = sum / 2;
    int[] dp = new int[t + 1];
    for (int i = 0; i < nums.length; i++) {
      for (int j = t; j >= nums[i]; j--) {
        dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
      }
    }
    return dp[t] == t;
  }
}

子集划分求解

我们知道任何一个集合的子集个数为\(2^n\),其中\(n\)表示集合当中数据的个数,比如说集合\({1, 2}\)有如下子集(4个):

\[\{空集\}, \{1\}, \{2\}, \{1, 2\} \]

我们先思考一下\(2^n\)是怎么计算出来的!我们在生成子集的时候对于每一个数据都有选择和不选择两种情况,这其实就变成了一个排列组合问题。在上面的例子当中1可选可不选(2),2可选可不选(2),因此总的集合个数为4,那么如果有n个数据的集合那么自己个数就等于:

\[2\times2\times2 \cdots = 2^n \]

这个选择情况的划分如下图所示:


而我们使用程序去计算一个集合的子集其实就是一个回溯的问题,分割等和子集的子集划分的代码如下:

public class Solution {
    private int target;
    public boolean canPartition(int[] nums) {

        int sum = 0;
        for (int num : nums) {
            sum += num;
        }
        if ((sum & 1) == 1 ) return false;
        // 我们最终的目标就是找到一个自己的和等于这个数
        target = sum / 2;
        return backTrace(-1, 0, nums);
    }

    /**
   * @param curIndex 表示当前遍历的数组的位置
   * @param curSum   当前集合所有数据的和
   * @param nums     待遍历的数组
   * @return
   */
    public boolean backTrace(int curIndex, int curSum, int[] nums) {
        if (curSum == target) return true;
        // 这里是剪枝操作 如果当前的和已经大于目标值或者
        // 遍历的下标超过数组的长度了就返回 false 表示
        // 这个分支没有找到一个集合,集合当中的数据之和等于 target
        else if (curSum > target || curIndex >= nums.length - 1) return false;
        // 选择某个数据
        curSum += nums[curIndex + 1];
        if (backTrace(curIndex + 1, curSum, nums))
            return true;
        // 不选择某个数据 对应这回溯的操作
        curSum -= nums[curIndex + 1];
        return backTrace(curIndex + 1, curSum, nums);
    }
}

我们知道动态规划的时间复杂度为\(O(nm)\),其中\(m\)表示nums数组的和的一半,\(n\)表示数组当中数据的个数,而这个使用生成子集的方法的话时间复杂度为\(O(2^n)\)。因此如果你再LeetCode上提交这个代码会超时。

总结

本文主要给大家介绍分割等和子集这个题目,这个题目的即可以使用动态规划进行求解也能使用回溯法进行求解,但是回溯法求解问题的时间复杂度太高。使用动态规划求解的方法还是比较抽象,可能需要大家花时间好好琢磨一下,希望大家有所收获,我是LeHung,我们下期再见!!!(记得点赞收藏哦!)


更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore

关注公众号:一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识。

有关01背包面试题系列(一)的更多相关文章

  1. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  2. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  3. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  4. 阿里云RDS——产品系列概述 - 2

    基础版云数据库RDS的产品系列包括基础版、高可用版、集群版、三节点企业版,本文介绍基础版实例的相关信息。RDS基础版实例也称为单机版实例,只有单个数据库节点,计算与存储分离,性价比超高。说明RDS基础版实例只有一个数据库节点,没有备节点作为热备份,因此当该节点意外宕机或者执行重启实例、变更配置、版本升级等任务时,会出现较长时间的不可用。如果业务对数据库的可用性要求较高,不建议使用基础版实例,可选择其他系列(如高可用版),部分基础版实例也支持升级为高可用版。基础版与高可用版的对比拓扑图如下所示。优势 性能由于不提供备节点,主节点不会因为实时的数据库复制而产生额外的性能开销,因此基础版的性能相对于

  5. Hive SQL 五大经典面试题 - 2

    目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类

  6. ruby - 从结束值创建一系列字符串 - 2

    我使用irb。下面是我写的代码。“斧头”..“bc”我期待"ax""ay""az""ba"bb""bc"但结果只是“斧头”..“bc”我该如何纠正?谢谢。 最佳答案 >puts("ax".."bc").to_aaxayazbabbbc 关于ruby-从结束值创建一系列字符串,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7617092/

  7. ruby-on-rails - 用一系列时间增量填充选择,加上其他选项 - 2

    使用RubyonRails,我使用给定的增量(例如每30分钟)用时间填充“选择”。目前我正在YAML文件中写出所有的可能性,但我觉得有一种更巧妙的方法。我想我想提供一个开始时间、一个结束时间、一个增量,并且目前只提供一个名为“关闭”的选项(想想“business_hours”)。所以,我的选择可能会显示:'Closed'5:00am5:30am6:00am...[allthewayto]...11:30pm谁能想出更好的方法,或者只是将它们全部“拼写”出来的最佳方法? 最佳答案 此答案基于@emh的答案。defcreate_hour

  8. Unity游戏开发:背包系统的实现 - 2

    背包是游戏中经常使用的一个组件,它负责管理玩家在游戏中所获得的道具。一个完整的背包系统应当具有将物品放置进背包、对背包内物品进行管理和使用背包内物品等功能。而往往一个背包系统的逻辑关系较为复杂,如果把所有功能都放在一个脚本中实现会使代码显得十分冗杂且缺乏逻辑。所以在背包系统的设计过程中,我们常将其分解为数据、逻辑和UI三部分分别来进行完成。一、UI设计以CottonPuzzle中的背包设计为例,我们需要有物品展示栏、物品切换按键和物品提示信息等部分。在Canvas中创建ItemHolder,在ItemHolder中创建LeftButton和RightButton控制物品的左右切换、Slot来控

  9. 蓝桥杯C/C++VIP试题每日一练之报时助手 - 2

    ?作者主页:静Yu?简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者?社区地址:前端知识交流社区?博主的个人博客:静Yu的个人博客?博主的个人笔记本:前端面试题个人笔记本只记录前端领域的面试题目,项目总结,面试技巧等等。接下来会更新蓝桥杯官方系统基础练习的VIP试题,依然包括解题思路,源代码等等。问题描述:给定当前的时间,请用英文的读法将它读出来。时间用时h和分m表示,在英文的读法中,读一个时间的方法是:  如果m为0,则将时读出来,然后加上“o’clock”,如3:00读作“threeo’clock”。  如果m不为0,则将时读出来,然后将分读出来,如5

  10. Spring Security 6.0系列【32】授权服务器篇之默认过滤器 - 2

    有道无术,术尚可求,有术无道,止于术。本系列SpringBoot版本3.0.4本系列SpringSecurity版本6.0.2本系列SpringAuthorizationServer版本1.0.2源码地址:https://gitee.com/pearl-organization/study-spring-security-demo文章目录前言1.OAuth2AuthorizationServerMetadataEndpointFilter2.OAuth2AuthorizationEndpointFilter3.OidcProviderConfigurationEndpointFilter4.N

随机推荐