leetcode——背包问题汇总

news2024/11/24 19:32:10

        本章来汇总一下leetcode中做过的背包问题,包括0-1背包完全背包

        背包问题的通常形式为:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。求解将哪些物品装入背包里物品价值总和最大。0-1背包和完全背包的区别就在于物品能否重复拿取

        但是一般题目不会明确告诉你是背包问题,需要自己将问题进行转化。下面汇总一些常见的0-1背包和完全背包问题。

0-1背包问题

1、分割等和子集(★)

         本题题意可以转化为:集合中能否出现总和为 sum / 2 的子集。 进而可以使用背包问题的思想来做:数组为背包,元素为物品。 又因为物品不可重复取,所以是0-1背包问题

        本题使用0-1背包动态规划算法思路如下:

  • 先求出nums数组的元素总和sum, (sum/2) 即为背包的大小。
  • 设dp数组,长度为sum/2 , 初始化为全0。dp[i]含义为:容量为i的背包最大能装多重的物品。
  • 先遍历物品再遍历背包,并且为了防止物品重复放入,背包需要倒序遍历。
  • 最后判断dp[target] 是否等于target,是则返回true。

        java代码如下:

class Solution {
    public boolean canPartition(int[] nums) {
        int sum = 0;
        for(int num : nums){
            sum += num;
        }
        if(sum % 2 != 0) return false; //总和为奇数,不可能分割。

        int target = sum / 2;
        int[] dp = new int[target+1];
        Arrays.fill(dp,0); //初始化dp数组为全0
        //先遍历物品再遍历背包
        for(int i=0; i<nums.length; i++){
            for(int j=target; j>=nums[i]; j--){
                dp[j] = Math.max(dp[j] , dp[j-nums[i]] + nums[i]);
            }
        }
        if(dp[target] == target) return true;
        return false;
    }
}

2、最后一块石头的重量II

        本题类似于上一题,可以转化为0-1背包问题。区别在于:本题如果可以分成等分量的两个子集,返回0;如果不能,则需要返回两子集的最小差值。 

        按照上一题的思路,java代码如下:

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;
        for(int stone : stones){
            sum += stone;
        }
        int target = sum / 2;
        int[] dp = new int[target+1];
        Arrays.fill(dp,0);
        for(int i=0; i<stones.length; i++){
            for(int j=target; j>=stones[i]; j--){
                dp[j] = Math.max(dp[j] , dp[j-stones[i]]+stones[i]);
            }
        }
        return sum-dp[target]*2;
    }
}

3、目标和(★)

        本题难点在于如何将题意转化为0-1背包问题。 可以这么想:将nums数组分为两个子集:子集一全加正号,子集二全加负号,然后像上题一样,两子集相碰撞(相减),结果为target值。设sum为总元素和,positive_sum为正数和,那么(sum-positive_sum)就为负数和,有如下等式成立:positive_sum - (sum-positive_sum) = target , 解得positive_sum = (target + sum) / 2 。

        此时成功将题意转化为0-1背包问题:positive_sum为背包大小,nums中元素为物品。 之前是要求背包所装物品的最大重量,而本题区别在于要求能装满背包的方法个数。整体思路不变,区别就在于递归公式要变化一下:

dp[j] = dp[j] + dp[j-nums[i]]

        这个递推公式用于求解装满背包的最大方法个数,可以记一下。

那么设的dp数组含义就变为:dp[i]表示装满大小为 i 的背包的方法个数

java代码如下:

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for(int num : nums){
            sum += num;
        }
        if((target + sum) % 2 != 0) return 0; //没有办法得到运算结果target
        int positive_sum = (target + sum) / 2; //正数和
        if(positive_sum < 0) return 0;//正数和不为负数
        int[] dp = new int[positive_sum+1];
        dp[0] = 1;
        for(int i=0; i<nums.length; i++){
            for(int j=positive_sum; j>=nums[i]; j--){
                dp[j] = dp[j] + dp[j-nums[i]];
            }
        }
        return dp[positive_sum];
    }
}

完全背包问题

1、零钱兑换II

        分析题意,有两个关键点:①求得是组合数,组合和排列不一样,组合没有顺序,而排列有。②硬币有无限个,因此本题为完全背包问题。 背包为总金额,物品为硬币,要求的是装满背包有多少种方法,和 上一题“目标和”要求的东西一样,因此递推公式为:

dp[j] = dp[j] + dp[j-nums[i]]

        本题是完全背包问题,物品个数为无限,因此遍历的第二层循环不是倒序遍历了,而是正序。(当初倒序遍历就是为了防止重复拿取物品,完全背包可以重复拿取物品)。

        遍历顺序也有讲究,先遍历物品再遍历背包求得是组合数,先遍历背包再遍历物品求得是排列数,此题要求组合数,因此先遍历物品再遍历背包。

        java代码如下:

class Solution {
    public int change(int amount, int[] coins) {
        int[] dp = new int[amount+1];
        dp[0] = 1;
        for(int i=0; i<coins.length; i++){
            for(int j=coins[i]; j<=amount; j++){
                dp[j] = dp[j] + dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
}

2、组合总和IV

        有了上述题的基础,本题可以说是手拿把掐了。 

        首先物品可以重复拿取,是完全背包问题题目求的是排列数(虽然题目说的组合数,但根据示例来看其实是求排列数),因此遍历顺序应该是先遍历背包再遍历物品 最后求的是装满背包的方法数,所以递推公式是:dp[j] = dp[j] + dp[j-nums[i]]。

        java代码如下:

class Solution {
    public int combinationSum4(int[] nums, int target) {
       int[] dp = new int[target+1];
       dp[0] = 1;
       for(int i=0; i<=target; i++){
           for(int j=0; j<nums.length; j++){
               if(i >= nums[j]){
                   dp[i] = dp[i] + dp[i-nums[j]];
               }
           }
       } 
       return dp[target];
    }
}

3、爬楼梯(★)

        本题是动态规划系列梦开始的地方,一开始使用斐波那契数列做的,现在一看就是个完全背包问题,背包大小是总楼梯阶数n,物品是1或2。 要求装满背包的方法个数。

       和上题可以说一毛一样,直接上代码:

class Solution {
    public int climbStairs(int n) {
        int dp[] = new int[n+1];
        dp[0] = 1;
        for(int i=0; i<=n; i++){
            for(int j=1; j<=2; j++){
                if(i >= j){
                    dp[i] = dp[i] + dp[i-j];
                }
            }
        }
        return dp[n];
    }
}

4、零钱兑换(★)

        本题属于完全背包问题,由于题目求得是凑成总金额所需得最少硬币个数,所以求组合数和排列数都可以,都不影响最后得最少硬币个数,因此遍历顺序先物品或者先背包都是可以的。

        本题由于求的是最少硬币个数,我们将dp[i]定义为:装满大小为 i 的背包所需最少物品个数。 递归公式为:

dp[j] = Math.min(dp[j] , dp[j-coins[i]]+1);

        此外,初始化dp数组的时候除了dp[0] 初始化为0之外,其他都要初始化为最大值。

        java代码如下:

class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount+1];//dp[j]:装满大小为j的背包所需的最少硬币个数
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0] = 0;
        for(int i=0; i<coins.length; i++){
            for(int j=coins[i]; j<=amount; j++){
                if(dp[j-coins[i]]!=Integer.MAX_VALUE){
                    dp[j] = Math.min(dp[j] , dp[j-coins[i]]+1);
                }
            }
        }
        return dp[amount] == Integer.MAX_VALUE? -1 : dp[amount];
    }
}

5、完全平方数(★)

        本题和 “零钱兑换”非常相似,同属于完全背包,并且求的也是装满背包所需的最小物品数。

        本题的背包为给定的整数n,物品为自然数的平方,如1的平方、2的平方......

        思路同上一题,java代码如下:

class Solution {
    public int numSquares(int n) {
        //背包:整数n | 物品:完全平方数的平方
        int[] dp = new int[n+1]; //dp[i]:装满大小为i的背包所需的最小物品数
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0] = 0;
        for(int i=1; i<=n; i++){
            for(int j=i*i; j<=n; j++){
                if(dp[j-i*i] != Integer.MAX_VALUE){
                    dp[j] = Math.min(dp[j] , dp[j-i*i]+1);
                }
            }
        }
        return dp[n];
    }
}

6、单词拆分(★)

        本题由于单词可以重复使用,因此是完全背包问题背包为字符串s,物品为wordDict里的字符串。   要求使用物品能否装满背包。 因此设dp数组,dp[i]的含义为:大小为i的背包能否被物品装满 由于物品(单词)之间有顺序之分,因此本题属于排列问题,先遍历背包再遍历物品

        直接看java代码:

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        int capacity = s.length();
        int[] dp = new int[capacity+1]; //dp[i]:大小为i的背包能否被物品装满
        dp[0] = 1;
        for(int i=1; i<=capacity; i++){
            for(String word : wordDict){
                int len = word.length();
                if(i>=len && dp[i-len]==1 && word.equals(s.substring(i-len,i))){
                    dp[i] = 1;
                    break;
                }
            }
        }
        return dp[capacity] == 1? true : false;
    }
}

总结

        以上题目过关背包问题基本上就没问题了,最后总结一下背包问题的套路

  • 0-1背包问题遍历背包的时候需要倒序遍历,以防止重复拿取物品。完全背包可以重复拿取物品,因此是正序遍历。
  • 题目求组合数和排列数的遍历顺序是不一样的:求组合数要先遍历物品再遍历背包,求排列数要先遍历背包再遍历物品。
  • 递归公式根据题目要求不一样而不一样,下面列举常见的几个递推公式:
    • 求尽可能装满背包的最大重量:dp[j] = Math.max(dp[j] , dp[j-nums[i]] + nums[i]); 如0-1背包的第1、2题。
    • 求装满背包的方法种数:dp[j] = dp[j] + dp[j-nums[i]]; 如0-1背包的第3题、完全背包的第1题、第2题、第3题。
    • 求装满背包的最小物品数:dp[j] = Math.min(dp[j] , dp[j-nums[i]]+1); 如完全背包的第4题、第5题。
    • 单词拆分这题单独记一下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1333035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

040、全卷积

之——FCN 目录 之——FCN 杂谈 正文 1.FCN 2.实现 杂谈 FCN&#xff08;Fully Convolutional Network&#xff09;是一种深度学习网络架构&#xff0c;专门设计用于语义分割任务。传统的深度学习网络如卷积神经网络&#xff08;CNN&#xff09;在处理图像时通常用于分类…

学生管理系统 数据库版结果 查询student表中所有学生信息

1.创建school_java数据库 CREATE DATABASE schooljava; USE schooljava; CREATE TABLE student ( id INT(11), name VARCHAR(25), tel INT(11), sex VARCHAR(6) ); DESC student; java代码 package Mysql; import java.sql.Connection; import java.sql.DriverManager; imp…

16-高并发-队列术

队列&#xff0c;在数据结构中是一种线性表&#xff0c;从一端插入数据&#xff0c;然后从另一端删除数据。 在我们的系统中&#xff0c;不是所有的处理都必须实时处理&#xff0c;不是所有的请求都必须实时反馈结果给用户&#xff0c;不是所有的请求都必须100%一次性处理成功…

ubuntu下docker安装,配置python运行环境

参考自: 1.最详细ubuntu安装docker教程 2.使用docker搭建python环境 首先假设已经安装了docker&#xff0c;卸载原来的docker 在命令行中运行&#xff1a; sudo apt-get updatesudo apt-get remove docker docker-engine docker.io containerd runc 安装docker依赖 apt-get…

041、基于CNN的样式迁移

之——基于CNN的滤镜 目录 之——基于CNN的滤镜 杂谈 正文 1.基于CNN的样式迁移 2.实现 杂谈 通过CNN的特征提取&#xff0c;可以实现将一个图片的样式模式特征迁移到另一张图像上。 正文 1.基于CNN的样式迁移 就是在某些层的输出上用其他的图片进行监督。 2.实现 一般来…

算法基础之完全背包问题

完全背包问题 核心思想&#xff1a;集合表示&#xff1a; f[i][j]表示前i种物品 总容量不超过j的最大价值 求f[i][j]时 分为选0、1、2……n个第i种物品 n种情况 每种情况为 f[i][j-kv] (取k个第i种物品) 即f[i][j] max(f[i-1][j] , f[i-1][j-v]w,f[i-1][j-2v]2w….f[i-1][j-k…

探索应用程序的指路明灯:Route 和 Router 入门指南(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

[MySQL binlog实战] 增量同步与数据搜索~从入门到精通

学习基础知识&#xff0c;并落实到实际场景&#xff08;增量同步数据搜索&#xff09; 对基础知识不感兴趣的&#xff0c;可以直接跳到应用场景 文章目录 binlog是什么简介产生方式文件格式statementrowmixed 怎么办开启 binlog查看 binlog其他查看相关命令运维查看 binlog设置…

ARM学习(24)Can的高阶认识和错误处理

笔者来聊一下CAN协议帧的认识和错误处理。 1、CAN协议帧认识 CAN 差分信号&#xff0c;是经过CAN收发器转成差分信号的&#xff0c;CAN RX和TX是逻辑电平。CAN的基础知识&#xff0c;可参考笔者这边文章&#xff1a;ARM学习&#xff08;21&#xff09;STM32 外设Can的认识与驱…

数据结构 | 查漏补缺

目录 数据的基本单位 冒泡排序 DFS和BFS中文 Prim 比较 中序线索二叉树 顺序栈 链栈 时间复杂度 循环队列 求第K个结点的值 数据的基本单位 数据元素 循环队列sq中&#xff0c;用数组elem[0‥25]存放数据元素&#xff0c;设当前sq->front为20&#xff0c;sq-&g…

谷歌推大语言模型VideoPoet:文本图片皆可生成视频和音频

Google Research最近发布了一款名为VideoPoet的大型语言模型&#xff08;LLM&#xff09;&#xff0c;旨在解决当前视频生成领域的挑战。该领域近年来涌现出许多视频生成模型&#xff0c;但在生成连贯的大运动时仍存在瓶颈。现有领先模型要么生成较小的运动&#xff0c;要么在生…

Vue 封装echarts柱状图(Bar)组件

目的&#xff1a;减少重复代码&#xff0c;便于维护 显示效果 组件代码 <template><div class"ldw-data-content-box"><div class"ldw-chilren-box"><div class"title" v-if"title">{{ title }}</div>…

软件测试5未来大发展路线,测试工程师发展偏向

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、软件测试的五大…

Python之classmethod和staticmethod的区别

python中3种方式定义类方法&#xff0c;常规方式、classmethod修饰方式、staticmethod修饰方式。 class A(object):def foo(self, x):print(调用foo函数 (%s, %s)%(self, x))print(self:, self)classmethoddef class_foo(cls, x):print(调用class_foo函数 (%s, %s) % (cls, x))…

【算法提升—力扣每日一刷】五日总结【12/18--12/22】

文章目录 2023/12/18LeetCode每日一刷&#xff1a;[20. 有效的括号](https://leetcode.cn/problems/valid-parentheses/) 2023/12/19LeetCode每日一刷&#xff1a;[150. 逆波兰表达式求值&#xff08;后缀表达式运算&#xff09;](https://leetcode.cn/problems/evaluate-rever…

博主自制丨免费下载丨免费使用丨仅用于测试

链接&#xff1a;点我立即下载 提取码&#xff1a;0j6h

CentOS 7 设置网络

CentOS 7 设置网络 正常情况 ①登陆进去之后使用下面的命令修改文件 echo ONBOOTyes >> /etc/sysconfig/network-scripts/ifcfg-ens33②如果是虚拟机重启后使用如下命令进行查看IP地址 ip addr注&#xff1a;到这里如果显示有两部分&#xff0c;则代表网络设置成功&a…

【OAuth】快速入门

一、引言 1、什么是OAuth2.0&#xff1f; OAuth 2.0是一个关于授权的开放网络协议&#xff0c;允许用户授权第三方应用访问其在服务提供商上存储的资源&#xff08;如照片、视频、联系人列表&#xff09;&#xff0c;而无需将用户名和密码提供给第三方应用。OAuth 2.0在第三方应…

【缓存清理工具】缓存清理软件_电脑缓存清理软件

产品介绍 有很多种比如来自网页和windows等,缓存如果不经常清理会使你的机器运行速度变慢&#xff0c;缓存清理工具可以帮你最多程度的清理垃圾文件而且速度也很快&#xff0c;有了它的帮助让你爱机清理彻底&#xff0c;运行更加顺畅&#xff01;主要能清理&#xff1a;所有应…

EA常见画图(类图、包图、构件图、状态图、顺序图、活动图)

EA常见活动图&#xff0c;状态图画法 类图:111&#xff08;1&#xff09;给关系添加注释&#xff08;2&#xff09;设置关系线样式 包图&#xff1a;&#xff08;1&#xff09;创建包图&#xff08;2&#xff09;在包中添加子包&#xff1a;&#xff08;3&#xff09;在包中添加…