DAY47:动态规划(十)零钱兑换Ⅱ+组合总和Ⅳ(完全背包求方案总数类型,排列+组合)

news2024/11/25 22:45:53

文章目录

    • 518.零钱兑换Ⅱ(装满背包方案数,本题是组合方案数)
      • 思路
      • DP数组含义
      • 递推公式
      • DP数组初始化
      • 遍历顺序(重要,不能颠倒)
        • 外层物品内层背包的情况
        • 外层背包内层物品的情况
        • 完全背包求排列数和组合数
      • 完整版
      • 总结+面试题目
    • 377.组合总和Ⅳ(本题是排列方案数)
      • 思路
      • DP数组含义
      • 递推公式
      • 初始化
      • 遍历顺序
      • 最开始的写法:int溢出
        • debug测试
      • 防止溢出的方式:对较大的数字取模
        • MOD取模的作用
      • 总结

这两道题都是方案数问题,注意方案数问题背包是装满的

518.零钱兑换Ⅱ(装满背包方案数,本题是组合方案数)

  • 本题就属于完全背包问题,但是for循环不可颠倒的情况
  • 本题求的是组合方案颠倒for的顺序就是排列方案

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。

题目数据保证结果符合 32 位带符号整数。

示例 1:

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:

输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3

示例 3:

输入:amount = 10, coins = [10] 
输出:1

提示:

  • 1 <= coins.length <= 300
  • 1 <= coins[i] <= 5000
  • coins 中的所有值 互不相同
  • 0 <= amount <= 5000

思路

本题提到了”凑成总和“,也就是从数组coins中选取物品,使得物品总重量和为目标和

因此我们第一反应还是背包问题。实际上,背包问题就是与得到某个确定的目标和有关的问题。

加上题目描述中说,假设每一种面额的硬币有无限个,因此这属于完全背包

本题最后返回的结果是可以凑成总金额的硬币组合数,也就是说装满背包一共有多少种方案。这就和 494.目标和 有点像,但是 目标和 是01背包。

本题和纯完全背包的区别在于,纯完全背包是求背包最大价值,而本题是求装满背包方案数。

DP数组含义

DP数组含义是看求的是什么。本题求的是装满背包的方案数,因此dp[j]表示装满容量为j的背包,有dp[j]种方法

递推公式

本题和01背包的 目标和 基本一样,也属于装满背包方案数目,因此也是同样的递推公式:

dp[j]+=dp[j-coins[i]];//装满背包方案数目问题的递推公式模板

DP数组初始化

和 目标和 那道题一样,求方案数目问题,最重要的就是一开始的初始化,因为所有递推都基于dp[0]

从DP数组的含义来考虑:

dp[0]含义是容量是0,多少种方案装满,因此dp[0]=1

从递推公式角度考虑:

dp[0]必须1,因为dp[0] = 1是方案类问题递归公式的基础。如果dp[0] = 0 的话,后面所有推导出来的值都是0了。

遍历顺序(重要,不能颠倒)

纯完全背包问题中,外层是物品还是背包都可以。但是本题,遍历顺序是不能颠倒的

纯完全背包求得装满背包的最大价值是多少,和凑成总和的元素有没有顺序没关系,有顺序也行,没有顺序也行。

但是,本题要求凑成总和的组合数,元素之间明确要求没有顺序。纯完全背包是能凑成总和就行,凑成总和的方案是否有顺序,不在考虑范围内。但是本题是求凑出来的方案个数,且每个方案个数是为组合数,不能有顺序

外层物品内层背包的情况

我们假设coins[0] = 1,coins[1] = 5,背包容量amount是6。

for(int i=0;i<coins.size();i++){//物品外层
    for(int j=coins[i];j<=amount;j++){
        dp[j]+=dp[j-coins[i]];
    }
}

对于物品在外,背包在内的情况:

  1. 先使用硬币1,计算出所有可能的组合。
  2. 然后加入硬币5,计算出所有可能的组合。

遍历物品i的DP数组如下所示:

// 初始化 DP 数组
dp = [1, 0, 0, 0, 0, 0, 0]

// 遍历硬币1
dp = [1, 1, 1, 1, 1, 1, 1]

// 遍历硬币5
dp = [1, 1, 1, 1, 1, 2, 2]

这个过程中,硬币5是在硬币1的基础上加入的,所以不会出现{5,1}这样的情况,只有{1,5}。

外层背包内层物品的情况

for(int j=0;j<=amount;i++){//背包容量外层
    for(int i=0;i<coins.size();i++){
        if(j>=coins[i])
        	dp[j]+=dp[j-coins[i]];
    }
}

对于背包在外,物品在内的情况:

  1. 对于每一个背包,都尝试使用硬币1和硬币5来填满背包。
  2. 这就意味着在计算一个特定的背包时,可能会有{1,5}和{5,1}两种情况。

推出来的DP数组如下所示:

在这里插入图片描述
我们可以看出来,结果确实是不对的,但是dp方法只能给出方案数,并不能把每个方案都打印出来。如果需要打印每个方案,必须使用回溯来解决,例如组合总和系列问题,因为需要打印组成结果,所以必须是回溯

回溯法的解释:【总结】用树形图和剪枝操作理解完全背包问题中组合数和排列数问题_先遍历物品后遍历背包是组合数_Calculus2022的博客-CSDN博客

对于coins = {1,2}, amount =4,先遍历物品的情况,相当于对先遍历背包的情况进行了剪枝

在这里插入图片描述
引用这张图能看的更清晰,我们如果先遍历物品,是物品1这条线全部遍历完,物品1遍历的时候不会涉及物品2 ,所以相当于剪枝剪掉了物品1底下1 2那种情况。而到了物品2,拆开2的时候,才会考虑2 1的情况。

由此可知,先遍历物品的情况,相当于对原排列数进行了剪枝

完全背包求排列数和组合数

完全背包问题中,物品和背包遍历的顺序决定了在求解时是考虑了物品的排列情况还是组合情况

物品在外,背包在内,得到的是组合方案数目

物品在内,背包在外,得到的是排列方案数目

完整版

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        //定义dp数组
        vector<int>dp(amount+1,0);
        //初始化
        dp[0]=1;
        for(int i=0;i<coins.size();i++){
            for(int j=coins[i];j<=amount;j++){
                dp[j]+=dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
};

总结+面试题目

完全背包问题,在求装满背包有几种方案的时候,认清遍历顺序是非常关键的。

如果求所有装满背包方案的组合数,就是外层for循环遍历物品,内层for遍历背包

如果求所有方案的排列数,就是外层for遍历背包,内层for循环遍历物品

01背包因为物品和背包遍历顺序不能颠倒,所以并不存在排列数和组合数的问题。

如果面试问到for嵌套顺序颠倒计算排列数和组合数的原理,可以这么回答:

物品在外的话,必须考虑完 i 的所有情况之后再考虑 i + 1,所以去掉了他们的排列关系; 而容量在外的话,每个容量的循环中可以尝试所有的物品,就像排列数每个位置可以放任何物品一样,这样就增加了物品的顺序

377.组合总和Ⅳ(本题是排列方案数)

  • 本题需要注意修改Int溢出的问题,dp[j]+=dp[j-nums[i]]可能会发生溢出,此时我们需要让运算结果对一个较大的数字来取模把dp[j]控制在这个较大数字的范围内

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

题目数据保证答案符合 32 位整数范围。

示例 1:

输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。

示例 2:

输入:nums = [9], target = 3
输出:0

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 1000
  • nums 中的所有元素 互不相同
  • 1 <= target <= 1000

思路

本质是本题求的是排列总和,而且仅仅是求排列总和的个数,并不是把所有的排列都列出来。为此,我们可以使用完全背包DP来解决。

如果本题要把排列都列出来的话,只能使用回溯算法暴力搜索。像39.组合总和 40.组合总和Ⅱ 都是必须列出组合结果,因此只能用回溯。

之前的组合系列:

39.组合总和:(这道题如果求方案数目是完全背包,且为组合数,物品在for外层

在这里插入图片描述
40.组合总和Ⅱ:(这道题求方案数目是01背包

在这里插入图片描述

DP数组含义

本题从示例可以看出,每个数字可以被使用无限次,所以是完全背包

dp[j]的含义是,装满容量为j的背包,有dp[j]种方案。

递推公式

本题依然是方案数目的递推公式:

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

初始化

方案数目类,初始化都是dp[0]=1,其余为0

遍历顺序

本题是求排列方案数目而不是组合,完全背包求排列方案数,背包外层遍历,物品内层遍历

具体原因上一道题目有分析。

最开始的写法:int溢出

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int>dp(target+1,0);
        dp[0]=1;
        //求排列数,外层背包容量
        for(int j=0;j<=target;j++){
            for(int i=0;i<nums.size();i++){
                if(j>=nums[i])
                    dp[j]+=dp[j-nums[i]];
            }
        }
		return dp[target];
    }
};

debug测试

测试结果出现了dp[j]+=dp[j-nums[i]];这一句,两数相加超过了int的情况。

在这里插入图片描述

防止溢出的方式:对较大的数字取模

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<long long>dp(target+1,0);
        dp[0]=1;
        long long MOD = INT_MAX; // adding modulo constant
        for(int j=0;j<=target;j++){
            for(int i=0;i<nums.size();i++){
                if(j>=nums[i]) {
                    dp[j]=(dp[j]+dp[j-nums[i]]) % MOD;
                }
            }
        }
        return dp[target];
    }
};

MOD取模的作用

这里注意,我们设置一个MOD,对MOD进行取模,目的就是把结果限制在<=MOD的范围内

因为取模运算,比如a/b,如果a<b的话,得到的就是a本身。如果a>b,得到的就是a/b的余数。

也就是,如果a=INT_MAX+1,b=INT_MAX,那么a%b就=1。

因此取模运算%,是一个限制数据范围的重要方法,它能保证结果始终在一定范围内

如果我们想把结果限定在MOD范围内,我们只需要result%MOD即可

总结

求装满背包有几种方法,递归公式都是一样的,没有什么差别,但关键在于遍历顺序!

本题与518.零钱兑换II就是一个鲜明的对比,一个是求排列,一个是求组合,遍历顺序完全不同。

同时,本题的取模运算也是一个非常重要的点,为了保证dp[j]不溢出,我们令MOD=INT_MAX,然后dp[j]%MOD,就可以dp[j]限制在INT_MAX范围内

这个操作和螺旋矩阵里面的(t+1)%4操作很像,螺旋矩阵中t初始值为0t=(t+1)%4能让无论多大的t,都在0 1 2 3这四个数字里面循环取值。要特别注意这种用法。(t只需要赋初值,别的都不需要管

[螺旋矩阵题目的两种解法及注意点_大磕学家ZYX的博客-CSDN博客](

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

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

相关文章

HTTP进化史:从HTTP1的简单到HTTP3的强大

文章目录 &#x1f4c8;I. HTTP1⚡A. 基本特点⚡B. 特点⚡C. 优缺点 &#x1f4c8;II. HTTP2⚡A. 基本特点⚡B. 特点⚡C. 优缺点 &#x1f4c8;III. HTTP3⚡A. 基本特点⚡B. 特点⚡C. 优缺点 &#x1f4c8;IV. 总结&#x1f4c8;附录&#xff1a;「简历必备」前后端实战项目&am…

资深测试整理,APP专项测试方法总结,看这篇就够了...

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

我们建议您关注另外两个趋势性漏洞。

即 Windows 错误报告服务 (CVE-2023-36874) 和 Windows MSHTML 平台 (CVE-2023-32046) 中的权限提升漏洞。现实生活中就有利用这两个漏洞的案例。 危险之处 要利用 CVE-2023-36874&#xff0c;攻击者需要访问目标计算机&#xff0c;并能够在普通用户默认具有有限权限的计算机上…

ESP32 LVGL:使用图标解决图片过大存不下的问题

背景 在LVGL中&#xff0c;用将图片转为C语言数组的方式储存的时候&#xff0c;图片转换的数组过大&#xff0c;当图片过多时会出现存不下的问题。 因此&#xff0c;可以使用字库图标解决该问题。 方法 将PNG图片转为字库文件 首先&#xff0c;我们将图片通过PS转为SVG矢量…

鸟类识别系统python+TensorFlow+Django网页界面+卷积网络算法+深度学习模型

一、介绍 鸟类识别系统&#xff0c;使用Python作为主要开发语言&#xff0c;基于深度学习TensorFlow框架&#xff0c;搭建卷积神经网络算法。并通过对数据集进行训练&#xff0c;最后得到一个识别精度较高的模型。并基于Django框架&#xff0c;开发网页端操作平台&#xff0c;…

XPath 的基本概念

XPath 的基本概念 引言 1. XPath 的基本概念 1.1 节点 1.2 路径表达式 1.3 轴 2. XPath 的语法和使用方法 2.1 标签定位 2.2 属性定位 2.3 文本定位 2.4 谓语和运算符 3. 示例演示 3.1 示例 1 &#xff1a; Web 自动化测试 3.2 示例 2 &#xff1a;数据提取和处理 3.3 示例 3 &…

pdsh 2.29 安装

下载&#xff1a; wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/pdsh/pdsh-2.29.tar.bz2解包&#xff1a; tar jxvf pdsh-2.29.tar.bz2 cd pdsh-2.29/安装&#xff1a; ./configure --prefix/u01/isi/pdsh-2.29/ --with-timeout6…

Spark编程-共享变量(广播变量和累加器)

共享变量是什么 Spark中的两个重要抽象一个是RDD&#xff0c;另一个就是共享变量。 在默认情况下&#xff0c;当Spark在集群的多个不同节点的多个任务上并行运行一个函数时&#xff0c;它会把函数中涉及到的每个变量&#xff0c;在每个任务上都生成一个副本。 但是&…

基于STM32 ARM+FPGA伺服控制系统总体设计方案(一)

设计需求 一套完整的伺服控制方案包括了上位机、驱控一体控制器和功率板三者。操作人员 通过上位机发送各种不同指令&#xff0c;然后控制器解析指令后执行相应的伺服功能&#xff0c;其次控 制器将驱动信号传输至功率板驱动电机&#xff0c;最后控制器采集反馈信息进行闭环…

为你带来全新的UGC体验!

当我们开始向更多 UGC 开放元宇宙&#xff0c;你们将有机会发现我们社区在 The Sandbox 中创造的一些令人惊叹的体验。 需要从你们自己的体验中获得灵感&#xff0c;或者只是想玩一些新东西&#xff1f;以下是我们的一些建造者几个月来的工作成果——现在就可以玩&#xff01; …

JVM学习

文章目录 1 JVM与Java体系结构1.0 Java发展重大事件1.1 虚拟机和Java虚拟机1.3 JVM整体结构1.4 Java代码执行流程1.5 JVM架构模型1.6 JVM的生命周期1.7 JVM发展历程 2 类加载子系统2.1 ClassLoader2.2 用户自定义类加载器2.2.1 为什么需要自定义类加载器2.2.2 自定义类加载器的…

Kong 服务和路由的添加

管理服务 这里参考DB-less-Mode&#xff0c;因为使用的是yaml配置文件的形式&#xff0c;所以所有的相关配置只需要往初始化的kong.yml文件中添加就可以了&#xff0c;就像nginx的配置文件 DB-less-Mode 创建服务 vim /etc/kong/kong.yml services: - name: my-service #…

排查思路----CPU占用过高

1、top查看cpu占用情况 %Cpu(s): 29.4 us, 24.1 sy, 0.0 ni, 10.1 id, 20.4 wa, 0.0 hi, 16.0 si, 0.0 st 发现wa和si都比较高 2、查wa高的问题 iostat -x 1 10%util 接近 100%&#xff0c;说明产生的I/O请求太多&#xff0c;I/O系统已经满负荷&#xff0c;该磁盘存在瓶颈。…

数据库性能优化中的表结构优化

数据库性能优化中的表结构优化 在数据库应用中&#xff0c;表结构的设计直接影响着数据库的性能。合理的表结构设计可以提高数据库的查询效率和性能&#xff0c;而不合理的表结构设计则可能导致查询效率低下、数据冗余、数据不一致等问题。因此&#xff0c;表结构优化是数据库…

智慧园区如何搭乘数字孪生这列快车?

无论是2022年的火爆的元宇宙还是今年出圈的ChatGPT&#xff0c;都体现着数字技术嵌入社会生活是大趋势&#xff0c;数字孪生作为智能技术的一大亮点&#xff0c;它在智慧园区中的应用会是怎样呢&#xff1f;今天我们就来聊一聊&#xff01; &#xff08;全文3000字&#xff0c;…

Star CCM+ 202206 (17.04) 详细安装步骤

首先下载win系统的安装包 阿里云盘&#xff1a; https://www.aliyundrive.com/s/WFfyvFhGxwK 提取码: x57w 百度云盘&#xff1a;https://pan.baidu.com/s/1qKgxYf2DGURCTW0rga8Xkw?pwdeiqi 提取码&#xff1a;eiqi &#xff08;资源来自网络&#xff0c;仅供个人学习交流&…

电脑提示找不到MSVCR120.dll 简单解决方法,亲测有效!

msvcr120.dll是windows系统的一部分&#xff0c;它是Windows操作系统中的一个动态链接库文件。该文件包含了一些在运行使用了C语言编写的程序时所需的函数和资源。当系统无法找到或加载msvcr120.dll文件时&#xff0c;会导致相关程序无法正常运行。 msvcr120.dll文件它提供了一…

25个高级SQL查询-基于特定排序标准对行进行排序

本专栏中的许多示例将基于以下员工表(employee)。只有少数例子将以其他表格为基础;在这些情况下,表格将与示例一起进行说明。 一、RANK 函数 RANK()函数是SQL中的窗口函数之一。窗口函数查看部分数据并计算该部分的结果。 RANK()函数根据提供的列为每一行指定一个等级…

C语言自定义类型详解(保姆级教学)

目录 1.结构体 1.1结构体的介绍 1.2结构体的声明 1.3特殊声明&#xff08;匿名结构体类型&#xff09; 1.4结构体自引用 1.5结构体变量的定义和初始化 1.6结构体内存对齐 1.7修改默认对其数 2.位段 2.1什么是位段 2.2位段的内存分配 3.枚举 3.1枚举的定义 3.2枚举…

第G1周:生成对抗网络(GAN)入门

目录 一、课题背景和开发环境二、理论基础1.生成器2. 判别器3. 基本原理 三、前期准备工作1. 定义超参数2.下载数据3. 配置数据 四、定义模型1. 定义鉴别器2. 定义生成器 五、训练模型1. 创建实例2. 训练模型3. 保存模型 &#x1f368; 本文为&#x1f517;365天深度学习训练营…