代码随想录算法训练营第四十三天丨 动态规划part06

news2024/11/25 4:43:42

518.零钱兑换II

思路

这是一道典型的背包问题,一看到钱币数量不限,就知道这是一个完全背包。

对完全背包还不了解的同学,可以看这篇:动态规划:关于完全背包,你该了解这些!(opens new window)

但本题和纯完全背包不一样,纯完全背包是凑成背包最大价值是多少,而本题是要求凑成总金额的物品组合个数!

注意题目描述中是凑成总金额的硬币组合数,为什么强调是组合数呢?

例如示例一:

5 = 2 + 2 + 1

5 = 2 + 1 + 2

这是一种组合,都是 2 2 1。

如果问的是排列数,那么上面就是两种排列了。

组合不强调元素之间的顺序,排列强调元素之间的顺序。 其实这一点我们在讲解回溯算法专题的时候就讲过了哈。

那我为什么要介绍这些呢,因为这和下文讲解遍历顺序息息相关!

回归本题,动规五步曲来分析如下:

  • 确定dp数组以及下标的含义

dp[j]:凑成总金额j的货币组合数为dp[j]

  • 确定递推公式

dp[j] 就是所有的dp[j - coins[i]](考虑coins[i]的情况)相加。

所以递推公式:dp[j] += dp[j - coins[i]];

这个递推公式大家应该不陌生了,卡哥在讲解01背包题目的时候在这篇494. 目标和 (opens new window)中就讲解了,求装满背包有几种方法,公式都是:dp[j] += dp[j - nums[i]];

  • dp数组如何初始化

首先dp[0]一定要为1,dp[0] = 1是 递归公式的基础。如果dp[0] = 0 的话,后面所有推导出来的值都是0了。

那么 dp[0] = 1 有没有含义,其实既可以说 凑成总金额0的货币组合数为1,也可以说 凑成总金额0的货币组合数为0,好像都没有毛病。

但题目描述中,也没明确说 amount = 0 的情况,结果应该是多少。

这里我认为题目描述还是要说明一下,因为后台测试数据是默认,amount = 0 的情况,组合数为1的。

下标非0的dp[j]初始化为0,这样累计加dp[j - coins[i]]的时候才不会影响真正的dp[j]

dp[0]=1还说明了一种情况:如果正好选了coins[i]后,也就是j-coins[i] == 0的情况表示这个硬币刚好能选,此时dp[0]为1表示只选coins[i]存在这样的一种选法。

  • 确定遍历顺序

本题中我们是外层for循环遍历物品(钱币),内层for遍历背包(金钱总额),还是外层for遍历背包(金钱总额),内层for循环遍历物品(钱币)呢?

我在动态规划:关于完全背包,你该了解这些! (opens new window)中讲解了完全背包的两个for循环的先后顺序都是可以的。

但本题就不行了!

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

而本题要求凑成总和的组合数,元素之间明确要求没有顺序。

所以纯完全背包是能凑成总和就行,不用管怎么凑的。

本题是求凑出来的方案个数,且每个方案个数是为组合数。

那么本题,两个for循环的先后顺序可就有说法了。

我们先来看 外层for循环遍历物品(钱币),内层for遍历背包(金钱总额)的情况。

代码如下:

for (int i = 0; i < coins.size(); i++) { // 遍历物品
    for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

假设:coins[0] = 1,coins[1] = 5。

那么就是先把1加入计算,然后再把5加入计算,得到的方法数量只有{1, 5}这种情况。而不会出现{5, 1}的情况。

所以这种遍历顺序中dp[j]里计算的是组合数!

如果把两个for交换顺序,代码如下:

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

背包容量的每一个值,都是经过 1 和 5 的计算,包含了{1, 5} 和 {5, 1}两种情况。

此时dp[j]里算出来的就是排列数!

可能这里很多同学还不是很理解,建议动手把这两种方案的dp数组数值变化打印出来,对比看一看!(实践出真知)

  • 举例推导dp数组

输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:

518.零钱兑换II

最后红色框dp[amount]为最终结果。

代码如下:

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 < dp.length; j++) {
                dp[j] += dp[j-coins[i]];
            }
        }
    return dp[amount];
    }
}

377. 组合总和 Ⅳ

思路

本题题目描述说是求组合,但又说是可以元素相同顺序不同的组合算两个组合,其实就是求排列!

弄清什么是组合,什么是排列很重要。

组合不强调顺序,(1,5)和(5,1)是同一个组合。

排列强调顺序,(1,5)和(5,1)是两个不同的排列。

在公众号里学习回溯算法专题的时候,一定做过这两道题目回溯算法:39.组合总和 (opens new window)和回溯算法:40.组合总和II (opens new window)会感觉这两题和本题很像!

但其本质是本题求的是排列总和,而且仅仅是求排列总和的个数,并不是把所有的排列都列出来。

如果本题要把排列都列出来的话,只能使用回溯算法爆搜

动规五部曲分析如下:

  • 确定dp数组以及下标的含义

dp[i]: 凑成目标正整数为i的排列个数为dp[i]

  • 确定递推公式

dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来。

因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的一部分。

在动态规划:494.目标和 (opens new window)和 动态规划:518.零钱兑换II (opens new window)中我们已经讲过了,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];

本题也一样。

  • dp数组如何初始化

因为递推公式dp[i] += dp[i - nums[j]]的缘故,dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。

至于dp[0] = 1 有没有意义呢?

其实没有意义,所以也不去强行解释它的意义了,因为题目中也说了:给定目标值是正整数! 所以dp[0] = 1是没有意义的,仅仅是为了推导递推公式。

至于非0下标的dp[i]应该初始为多少呢?

初始化为0,这样才不会影响dp[i]累加所有的dp[i - nums[j]]。

  • 确定遍历顺序

个数可以不限使用,说明这是一个完全背包。

得到的集合是排列,说明需要考虑元素之间的顺序。

本题要求的是排列,那么这个for循环嵌套的顺序可以有说法了。

在动态规划:518.零钱兑换II (opens new window)中就已经讲过了。

如果求组合数就是外层for循环遍历物品,内层for遍历背包

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

如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举一个例子:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!

所以本题遍历顺序最终遍历顺序:target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历

  • 举例来推导dp数组

我们再来用示例中的例子推导一下:

377.组合总和Ⅳ

如果代码运行处的结果不是想要的结果,就把dp[i]都打出来,看看和我们推导的一不一样。

以上分析完毕,代码如下:

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target+1];
        dp[0]=1;
        for (int i = 0; i < dp.length; i++) {
            for (int j = 0; j < nums.length; j++) {
                if (i >= nums[j]) {//如果当前背包的容量大于该物品所需要的容量,就进行累加
                    dp[i] += dp[i - nums[j]];
                }
            }
        }
        return dp[target];
    }
}

完全背包感觉还不错,在逻辑上完全能够进行理解、只是在细节上需要注意~~~

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

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

相关文章

苹果IOS系统webglcontextlost问题-解决方案

问题描述 在IOS手机 解码视频流的时候&#xff0c;第一次可以正常播放&#xff0c;但只要IOS手机熄屏&#xff0c;再重新唤醒&#xff0c;就会一直播放失败&#xff0c;无论换哪个浏览器都不行。安卓手机则一切正常。 经过排查&#xff0c;发现 IOS手机 的浏览器会无故 webGL…

大聪明教你学Java | Win10 环境下安装部署 RocketMQ

前言 &#x1f34a;作者简介&#xff1a; 不肯过江东丶&#xff0c;一个来自二线城市的程序员&#xff0c;致力于用“猥琐”办法解决繁琐问题&#xff0c;让复杂的问题变得通俗易懂。 &#x1f34a;支持作者&#xff1a; 点赞&#x1f44d;、关注&#x1f496;、留言&#x1f4…

Word里MathType插件符号表消失了

场景再现 在word里面编辑数学公式&#xff0c;结果符号表跑到屏幕外面了&#xff0c;找不到&#xff1b; 解决办法 在其中找到视图->工具栏&#xff0c;点击即可&#xff1a; 还嫌弃它跑到外边了&#xff0c;那就可以点击符号表的边框&#xff1a; 双击左边边框&#x…

YOLO目标检测——夜间车辆检测数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;智能交通监控系统、自动驾驶系统、夜间行车安全辅助系统等数据集说明&#xff1a;夜间车辆检测数据集&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标注框质量高&#xff0…

如何进行性能测试?

性能测试是软件测试中的一个重要环节&#xff0c;其目的是评估系统在不同负荷下的性能表现&#xff0c;包括响应时间、吞吐量、并发数等指标。通常可以通过以下几种方法进行性能测试&#xff1a; 1、负载测试 负载测试是模拟多用户同时访问系统&#xff0c;测试系统在高并发、…

云产品FC免费试用获取奖励步骤

文章目录 1、获取活动链接2、报名参加3、试用产品领取产品试用权限部署生成小说提交作品 4、提交任务获取奖励 1、获取活动链接 活动时间2023.11.1&#xff5e;2023.11.30 名额有限&#xff0c;先到先得 进群群主获取活动链接 2、报名参加 直接点击链接进入小程序进行登录授…

备份doris数据到minio

1、MINIO 设置 创建服务账户&#xff0c;记住ACCESS_KEY和SECRET_KEY 创建Buckets doris 设置region 在首页查看服务ip和端口号 2、创建S3备份库 因为minio是兼容S3协议的&#xff0c;所以可以通过s3协议链接minio。 CREATE REPOSITORY minio WITH S3 ON LOCATION "s3://…

表格冻结第二行

在网上找了一圈也没找到说明白的。 像这样的表格下面会有很多行&#xff0c;往下翻的时候会忘记第二行显示的什么内容。 需求&#xff1a;将第二行进行冻结 实现&#xff1a; 1&#xff0c;选中第一和第二行&#xff0c;下拉&#xff0c;点击冻结 2&#xff0c;会显示冻结至…

UI设计感大型数据管理仪表盘后台模板源码

大型数据管理仪表盘后台模板是一款适合数据统计管理后台网站模板下载。提示&#xff1a;本模板调用到谷歌字体库&#xff0c;可能会出现页面打开比较缓慢。 演示下载 qnziyw点cn/wysc/qdmb/20838点html

5+非肿瘤+细胞凋亡相关生信思路,请自行查阅

今天给同学们分享一篇生信文章“Genome-wide identification and functional analysis of dysregulated alternative splicing profiles in sepsis”&#xff0c;这篇文章发表在J Inflamm (Lond)期刊上&#xff0c;影响因子为5.1。 结果解读&#xff1a; 脓毒症患者和健康对照…

Python爬虫实战-批量爬取下载网易云音乐

大家好&#xff0c;我是python222小锋老师。前段时间卷了一套 Python3零基础7天入门实战https://blog.csdn.net/caoli201314/article/details/1328828131小时掌握Python操作Mysql数据库之pymysql模块技术https://blog.csdn.net/caoli201314/article/details/133199207一天掌握p…

New Maven Project

下面两个目录丢失了&#xff1a; src/main/java(missing) src/test/java(missing) 换个JRE就可以跑出来了 变更目录

jmeter接口自动化测试通过csv文件读取用例并执行测试

最近在公司测试中经常使用jmeter这个工具进行接口自动化&#xff0c;简单记录下~ 一、在csv文件中编写好用例 首先在csv文件首行填写相关参数&#xff08;可根据具体情况而定&#xff09;并编写测试用例。脚本可通过优先级参数控制执行哪些接口&#xff0c;通过端口参数同时执…

后端接口接收对象和文件集合,formdata传递数组对象

0 问题 后端接口需要接收前端传递过来的对象和文件集合&#xff1b;对象中存在数组对象 1 前端和后端 前端只能使用formdata来传递参数&#xff0c;后端不使用RequestBody注解 2 formdata传递数组对象 2.1 多个参数对象数组 addForm: {contactInfo: [{contactPerson: ,…

FLASH语音芯片和MP3音乐芯片的不同之处

语音芯片除了不可重复擦写的otp语音芯片之外还有内置flash可重复擦写的语音芯片&#xff0c;还有音质相对来说更好的mp3音乐芯片&#xff0c;接下来让我们就flash芯片和MP3芯片来展开讨论&#xff0c;看看两者之间究竟有哪些不同之处。 flash语音芯片 Flash语音芯片是可以进行…

视频转序列图片:掌握技巧,轻松转换

随着社交媒体和视频平台的日益普及&#xff0c;视频已成为我们生活中不可或缺的一部分。有时&#xff0c;我们需要将视频转换为图片序列&#xff0c;例如制作GIF动图或提取视频中的特定画面。现在一起来看云炫AI智剪如何将视频转换为序列图片&#xff0c;并轻松实现转换。 操作…

【GEE】1、Google 地球引擎简介

1简介 在本模块中&#xff0c;我们将讨论以下概念&#xff1a; 定义 Google 地球引擎中的主要数据类型以及如何使用它们。 如何探索数据集并限制特定研究站点的输出。 如何可视化火灾前后景观之间光合活动的差异。 2背景 遥感可以成为生态学家在更大空间尺度上了解研究系统…

如何用维格云搭建和一键训练你的钧瓷AI机器人?

大禹智库 第69期(总第400期) 2023年11月4日 如何用维格云搭建和一键训练你的钧瓷AI机器人? 钧瓷私有数据聊天机器人是一种能够根据预设的数据集进行智能对话的机器人。通过维格云,我们可以轻松地搭建自己的钧瓷私有数据聊天机器人。本文将以钧道机器人为例,详细介绍如何…

【C语言初学者周冲刺计划】2.4求一个3×3矩阵对角线元素之和

1解题思路&#xff1a; 首先学会怎么输入3*3矩阵的数值&#xff1b;然后细节处&#xff0c;比如最后求和应该加什么&#xff0c;怎么写问题&#xff1b; 2代码&#xff1a; #include<stdio.h> int main() {int str[3][3],i,j,sum0;for(i0;i<3;i){for(j0;j<3;j){…

过去60年145项全球开源系统杰出成果颁布,百度飞桨登榜!

近日&#xff0c;BenchCouncil&#xff08;国际测试委员会&#xff09;颁布首个开源系统杰出成果榜&#xff08;1960s-2021&#xff09;&#xff0c;评选了开源方面具有巨大影响并对软硬件发展产生重大推动作用的顶级成果。百度飞桨深度学习框架PaddlePaddle成功上榜。 BenchCo…