【动态规划】完全背包问题应用

完全背包问题应用

  • 1.零钱兑换
  • 2.零钱兑换 II
  • 3.完全平方数

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.零钱兑换

题目链接: 322. 零钱兑换

题目分析:

在这里插入图片描述
有一堆硬币,从这些硬币中选一些硬币,看能凑成总金额amount所需的 最少的硬币个数。这是一个典型的背包问题。你可以认为每种硬币的数量是无限的,这句话很重要。看到这个无限你就要想到这是一个完全背包问题。

算法原理:

1.状态表示

dp[i][j] 表示:从前 i 个硬币中挑选,总和正好等于j,所有选法中,最少硬币个数。

在这里插入图片描述

2.状态转移方程

根据最后一个位置,划分情况

不选 i,说明所有选法中都不包含第 i 个硬币,相当于从去 1 ~ i - 1 这个区间去选,就是dp[i-1][j]

在这里插入图片描述
选1个i,这时就有 i 这个硬币了,然后仅需去 1 ~ i - 1 区间去选一个 不超过j - coins[i]的最少硬币个数,然后在加上 i 这个硬币个数

在这里插入图片描述

同理,选2个 i、选3个 i都是上面的分析思路

在这里插入图片描述

发现填一个状态的时候发现这个状态时候很多状态拼接而成的,这个时候我们要想到策略把这些状态用一个或者两个状态来表示。

在完全背包哪里我们已经分析过了,这里直接写,然后我们取所有情况的最小值。

在这里插入图片描述

3.初始化

  1. 多开一行一列
  2. 里面的值要保证后序的填表是正确的
  3. 下标的映射关系

第一列不用初始化,因为用到dp[i][j-coins[i]] 前提 j >= coins[i],所以不会越界。我们只初始化第一行。

第一行表示硬币为空,当 j = 0表示总和为0,不选就行了

在这里插入图片描述
当 j = 1、2、3…,硬币为空,根本凑不出总和是这些的情况。之前完全背包说过这些都给-1然后特判一下,但是在优化的时候有说过这些无效的情况仅需不让它们参与我们求dp[i][j]就可以了,这里我们求得是min,因此我们可以给这些位置初始化为无穷大,这样即使情况不存在求min也不会影响。注意这里给无穷大不能给INT_MAX,首先 INT_MAX + 1 会越界Leetcode会报错,其次INT_MAX + 1 会是一个很小的数求min就会用到它。这里我们可以给0x3f3f3f3f,可以保证在这个算法中它是最大的,并且用来做加法也不会越界。

在这里插入图片描述

4.填表顺序

填dp[i][j]会用到上面和左边的值,因此从上往下填写每一行,每一行从左往右。

在这里插入图片描述

5.返回值

dp[i][j] 表示:从前 i 个硬币中挑选,总和正好等于j,所有选法中,最少硬币个数。我们要的是从整个数组中选,总和等于amount,最少硬币个数,因此返回dp[n][amount],但是可能整个数组也凑不出总和等于amount,所以返回判断一下
dp[n][amount] >= INF ? : -1 : dp[n][amount]

在这里插入图片描述

class Solution {
public:int coinChange(vector<int>& coins, int amount) {// 1.创建 dp 表// 2.初始化// 3.填表// 4.返回值const int INF = 0x3f3f3f3f;int n = coins.size();vector<vector<int>> dp(n + 1, vector<int>(amount + 1));for(int j = 1; j <= amount; ++j) dp[0][j] = INF;for(int i = 1; i <= n; ++i)for(int j = 0 ; j <= amount; ++j){dp[i][j] = dp[i - 1][j];if(j >= coins[i - 1])dp[i][j] = min(dp[i][j], dp[i][j - coins[i - 1]] + 1);}return dp[n][amount] >= INF ? -1 : dp[n][amount];}
};

优化

class Solution {
public:int coinChange(vector<int>& coins, int amount) {// 1.创建 dp 表// 2.初始化// 3.填表// 4.返回值const int INF = 0x3f3f3f3f;// int n = coins.size();// vector<vector<int>> dp(n + 1, vector<int>(amount + 1));// for(int j = 1; j <= amount; ++j) dp[0][j] = INF;// for(int i = 1; i <= n; ++i)//     for(int j = 0 ; j <= amount; ++j)//     {//         dp[i][j] = dp[i - 1][j];//         if(j >= coins[i - 1])//             dp[i][j] = min(dp[i][j], dp[i][j - coins[i - 1]] + 1);//     }// return dp[n][amount] >= INF ? -1 : dp[n][amount];//优化int n = coins.size();vector<int> dp(amount + 1,INF);dp[0] = 0;for(int i = 1; i <= n; ++i)for(int j = coins[i - 1]; j <= amount; ++j)dp[j] = min(dp[j], dp[j - coins[i - 1]] + 1);return dp[amount] >= INF ? -1 : dp[amount];}
};

2.零钱兑换 II

题目链接:518. 零钱兑换 II

题目分析:

在这里插入图片描述

当看到硬币有无限个,我们就该想到这是一道完全背包问题。

算法原理:

1.状态表示

dp[i][j] 表示:从前 i 个硬币中挑选,总和正好等于j,一共有多少种选法

2.状态转移方程

根据最后一个位置,划分情况

不选 i,说明所有选法中都不包含第 i 个硬币,相当于从去 1 ~ i - 1 这个区间去选,就是dp[i-1][j]

在这里插入图片描述

选1个i,这时就有 i 这个硬币了,然后仅需去 1 ~ i - 1 区间去选一个 总和正好等于j - coins[i],注意问的是多少种选法, i 这个硬币仅需跟在所有选法后面就行了,不需要加1。

选2个i,选3个i … 都是类似分析

在这里插入图片描述

发现填一个状态的时候发现这个状态时候很多状态拼接而成的,这个时候我们要想到策略把这些状态用一个或者两个状态来表示。

在完全背包哪里我们已经分析过了,这里直接写。
注意这里直接写,是因为前面已经分析过了,这个状态只适用于完全背包。
在这里插入图片描述

最后我们将所有情况加起来,注意 j-coins[i] 不一定存在。必须要满足 j >= coins[i],才能用这个状态。

在这里插入图片描述

3.初始化

  1. 多开一行一列
  2. 里面的值要保证后序的填表是正确的
  3. 下标的映射关系

第一列不用初始化,因为用到dp[i][j-coins[i]] 前提 j >= coins[i],所以不会越界。我们只初始化第一行。

第一行表示硬币为空,当 j = 0表示总和为0,不选就行了,这是一种选法

在这里插入图片描述

当j = 1、2、3…,硬币为空根本凑不出总和等于j的选法。直接都给0就行了

在这里插入图片描述

4.填表顺序

填dp[i][j]会用到上面和左边的值,因此从上往下填写每一行,每一行从左往右。

5.返回值

dp[i][j] 表示:从前 i 个硬币中挑选,总和正好等于j,一共有多少种选法,我们要的是从整个数组种选总和等于amount一共有多少种选法,正好就是dp[n][amount]

class Solution {
public:int change(int amount, vector<int>& coins) {// 1.创建 dp 表// 2.初始化// 3.填表// 4.返回值// int n = coins.size();// vector<vector<int>> dp(n + 1, vector<int>(amount + 1));// dp[0][0] = 1;// for(int i = 1; i <= n; ++i)//     for(int j = 0; j <= amount; ++j)//     {//         dp[i][j] = dp[i - 1][j];//         if(j >= coins[i - 1])//             dp[i][j] += dp[i][j - coins[i - 1]];//     }// return dp[n][amount];//优化// int n = coins.size();// vector<int> dp(amount + 1);// dp[0] = 1;// for(int i = 1; i <= n; ++i)//    for(int j = coins[i - 1]; j <= amount; ++j)//        dp[j] += dp[j - coins[i - 1]];// return dp[amount];// vector<int> dp(amount + 1);// dp[0] = 1;// for(int i = 0; i < coins.size(); ++i)//用不到i//     for(int j = coins[i]; j <= amount; ++j)//         dp[j] += dp[j - coins[i]];// return dp[amount];vector<int> dp(amount + 1);dp[0] = 1;for(auto x : coins)for(int j = x; j <= amount; ++j)dp[j] += dp[j - x];return dp[amount];}
};

3.完全平方数

题目链接: 279. 完全平方数

题目分析:

在这里插入图片描述

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。

完全平方数:比如说1^2 = 1,2^2 = 4,3^2 = 9。1、4、9就是完全平方数。

在这里插入图片描述

算法原理:

从左往右开始挑完全平方数,一个数可以挑多次,挑出来的数只要等于n即可。如果对背包问题比较敏感,这就是背包问题,并且是完全背包问题。

1.状态表示

dp[i][j] 表示:从前 i 个完全平方数种挑选,总和正好等于 j,所有选法中,最少的数量

在这里插入图片描述

2.状态转移方程

根据最后一个位置,划分情况

不选 i^2,说明所有选法中都不包含 i^2 这个平方数,相当于从去 1 ~ i - 1 这个区间去选,就是dp[i-1][j]

在这里插入图片描述

选1个 i^2,然后在去1 ~ i - 1区间挑一个总和等于 j - i^2的最少数量,然后在加上选的 i^2这一个数。

同理选2个 i^2,3个 i^2…, 和上面分析一样

在这里插入图片描述

发现填一个状态的时候发现这个状态时候很多状态拼接而成的,这个时候我们要想到策略把这些状态用一个或者两个状态来表示。

在完全背包哪里我们已经分析过了,这里直接写。

在这里插入图片描述
然后从所有情况找最小值

在这里插入图片描述

3.初始化

  1. 多开一行一列
  2. 里面的值要保证后序的填表是正确的
  3. 下标的映射关系

第一列不用初始化,因为用到dp[i][j-coins[i]] 前提 j >= coins[i],所以不会越界。我们只初始化第一行。

第一行表示完全平方数为0,当 j = 0表示总和为0,不选就行了,最少数量为0

在这里插入图片描述
当 j = 1、2…,完全平方数为0,根本凑不出和为 j,然后我们填dp[i][j]要最小值,为了不让这些位置得值影响填表,因此可以给0x3f3f3f3f

在这里插入图片描述

4.填表顺序

填dp[i][j]会用到上面和左边的值,因此从上往下填写每一行,每一行从左往右。

在这里插入图片描述

5.返回值

dp[i][j] 表示:从前 i 个完全平方数中挑选,总和正好等于 j,所有选法中,最少的数量,但是我们要的是从整个完全平方数种选总和正好等于n最少数量。这里有一个问题 i 取到哪里比较好呢?

我们绝对不会选一个完全平方数会比n大,所以说下标的平方应该小于等于n

所以我们最终返回的是 dp[√n][n],同时建表的时候也应该是(√n + 1)*(n + 1)规模的。

在这里插入图片描述

class Solution {
public:int numSquares(int n) {// int m = sqrt(n);// vector<vector<int>> dp(m + 1, vector<int>(n + 1));// for(int j = 1; j <= n; ++j) dp[0][j] = 0x3f3f3f3f;// for(int i = 1; i <= m; ++i)//     for(int j = 0; j <= n; ++j)//     {//         dp[i][j] = dp[i - 1][j];//         if(j >= i * i)//             dp[i][j] = min(dp[i][j], dp[i][j - i * i] + 1);//     }// return dp[m][n];//优化int m = sqrt(n);vector<int> dp(n + 1,0x3f3f3f3f);dp[0] = 0;for(int i = 1; i <= m; ++i)for(int j = i * i; j <= n; ++j)dp[j] = min(dp[j], dp[j - i * i] + 1);return dp[n];}
};

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

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

相关文章

Github 优质项目推荐(第七期)

文章目录 Github优质项目推荐 - 第七期一、【LangGPT】&#xff0c;5.7k stars - 让每个人都成为提示专家二、【awesome-selfhosted】&#xff0c;198k stars - 免费软件网络服务和 Web 应用程序列表三、【public-apis】&#xff0c;315k stars - 免费 API四、【JeecgBoot】&am…

JVM(HotSpot):直接内存及其使用建议

文章目录 一、什么是直接内存&#xff1f;二、特点三、使用案例四、直接内存的管理 一、什么是直接内存&#xff1f; Direct Memory&#xff1a;系统内存 普通IO&#xff0c;运行原理图 磁盘到系统内存&#xff0c;系统内存到jvm内存。 NIO&#xff0c;运行原理图 划分了一块…

结合seata和2PC,简单聊聊seata源码

当前代码分析基于seata1.6.1 整体描述 整体代码流程可以描述为 TM开启全局事务&#xff0c;会调用TC来获取XID。TC在接收到通知后&#xff0c;会生成XID&#xff0c;然后会将当前全局事务保存到global_table表中&#xff0c;并且返回XID。在获取到XID后&#xff0c;会执行业务…

selenium的IDE插件进行录制和回放并导出为python/java脚本(10)

Selenium IDE&#xff1a;Selenium Suite下的开源Web自动化测试工具&#xff0c;是Firefox或者chrome的一个插件&#xff0c;具有记录和回放功能&#xff0c;无需编程即可创建测试用例&#xff0c;并且可以将用例直接导出为可用的python/java等编程语言的脚本。 我们以chrome浏…

Vue3嵌套导航相对路径问题

有如下的页面设计&#xff0c;页面上方第一次导航&#xff0c;两个菜单&#xff0c;首页和新闻 点击新闻&#xff0c;内容里面嵌套一个左侧和右侧&#xff0c;左侧有4条新闻&#xff0c;点击某一条新闻&#xff0c;右侧显示详情 代码如下&#xff1a; ​ File Path: d:\hello\…

自感式压力传感器结构设计

自感式压力传感器的结构如图2-35 和图 2-36所示&#xff0c;分为变隙式、变面积式和螺管式三种&#xff0c;每种均由线网、铁心和衔铁三部分组成。 图2-35 自感式压力传感器的结构 1-线圈 2-铁心 3-衔铁 图2-36 螺管式 1-线图 2-铁心 3一衔铁 自感式压力传感器按磁路变化可…

QT的核心机制 对话框资源

案例 1、键盘按下w&#xff0c;s&#xff0c;a&#xff0c;d键分别为标签向上&#xff0c;下&#xff0c;左&#xff0c;右移动 鼠标按下获取本地坐标&#xff0c;全局坐标 鼠标双击获取本地坐标&#xff0c;全局坐标 鼠标移动获取本地坐标&#xff0c;全局坐标 让鼠标跟踪…

Midjourney零基础学习

Midjourney学习笔记TOP04 Midjourney的各种参数设置 Midjourney的用户操作界面没有醒目的工具栏、属性栏&#xff0c;所有的操作都是通过调用各种指令和参数进行的。 【MJ Version】 Midjourney在2023年3月份就已经更新到了V5版本&#xff0c;V5版本除了画质有所提升外&#…

interwirelessac9560感叹号,电脑无法连接wifi,无法搜索到wifi

interwirelessac9560感叹号 电脑无法连接wifi&#xff0c;无法搜索到wifi 原因 这可能是wifl模块出现了问题。 解决方案 1、winx 打开&#xff0c;选择【设备管理器】 2、选择网络适配器 右键打开wireless-AC&#xff0c;选择【卸载设备】。 3、关机2分钟后&#xff0c…

SpringBoot智慧外贸平台

专业团队&#xff0c;咨询就送开题报告&#xff0c;欢迎大家私信留言&#xff0c;联系方式在文章底部 摘 要 网络的广泛应用给生活带来了十分的便利。所以把智慧外贸管理与现在网络相结合&#xff0c;利用java技术建设智慧外贸平台&#xff0c;实现智慧外贸的信息化。则对于进…

数据结构-5.9.树的存储结构

一.树的逻辑结构&#xff1a; 二.双亲表示法(顺序存储)&#xff1a; 1.树中除了根结点外每一颗树中的任意一个结点都只有一个父结点(双亲结点)&#xff1b; 2.结点包括结点数据和指针&#xff1b; 3.上述图片中右边的顺序存储解析&#xff1a;比如A结点左边的0&#xff0c;就…

ASML业绩暴雷,股价一度跌超16%

KlipC报道&#xff1a;当地时间10月15日&#xff0c;阿斯麦&#xff08;ASML&#xff09;原定于周三公布的三季度业绩报告由于技术原因被短暂地提前公布&#xff0c;业绩报告显示&#xff0c;阿斯麦第三季度总净销售额75亿欧元&#xff0c;毛利率50.8%&#xff0c;净利润21亿欧…

社招高频面试题

1.单例模式 面试突击50&#xff1a;单例模式有几种写法&#xff1f; 2.Mybatis缓存机制 MyBatis的一、二级缓存查询关系 一级缓存是SqlSession级别&#xff0c;不能跨SqlSession共享&#xff0c;默认开启。 二级缓存是基于mapper namespace级别的&#xff0c;可以跨SqlSessi…

Scala入门基础(10)高级函数

一.什么是高阶函数 二.map函数 三.foreach函数 四.filter函数 五.flatten函数 正文&#xff1a; 一.什么是高阶函数 高阶函数&#xff1a;是一个特殊的函数&#xff0c;特殊之处在于&#xff1a;它指使用其他函数作为参数或返回值 &#xff08;演示&#xff09; 二.map函…

SpringSecurity(一)——认证实现

一、初步理解 SpringSecurity的原理其实就是一个过滤器链&#xff0c;内部包含了提供各种功能的过滤器。 当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序。 核心过滤器&#xff1a; &#xff08;认证&#xff09;UsernamePasswordAuthenticationFilter:负责处理…

python yolov8半自动标注

首先标注一部分图片&#xff0c;进行训练&#xff0c;生成模型&#xff0c;标注文件为xml方便后面统一做处理。 1、标注数据&#xff08;文件为xml, 转为txt用于训练&#xff0c;保留xml标签文件&#xff09; 2、模型训练&#xff08;训练配置、训练代码、&#xff09; 3、使用…

极狐GitLab 发布安全补丁版本 17.4.1、17.3.4、17.2.8

GitLab 是一个全球知名的一体化 DevOps 平台&#xff0c;很多人都通过私有化部署 GitLab 来进行源代码托管。极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门为中国程序员服务。可以一键式部署极狐GitLab。 学习极狐GitLab 的相关资料&#xff1a; 极狐GitLab 官网极狐…

[已解决]DockerTarBuilder永久解决镜像docker拉取异常问题

前阵子发现阿里云的docker加速镜像失效了&#xff08;甚至连nginx都拉取不了&#xff09;&#xff0c;重新换了并且加多了网络上比较常用的dokcer加速源&#xff0c;可以解决一部分问题&#xff0c;但仍然有一些镜像的某个版本或一些比较冷的镜像就是拉取不了&#xff0c;原因未…

『网络游戏』数据库表格转储【25】

避免勿删数据库表格&#xff0c;可以将表格存储 放到桌面即可 现在将表格删除后点击 浏览桌面表格保存即可 修改客户端脚本&#xff1a;NetSvc.cs 目的是在数据库更新异常时弹出提示以便修改 本章结束

进程间通信、无名管道、有名管道

一、进程 1.1 进程间通信的概念 线程通信通过全局变量即可。 进程间通信是相互独立的&#xff0c;但是所有进程都共用一份内核空间&#xff0c;所以进程和进程之间的通信可以通过内核去进行。 1.2 进程间通信方式 共7种: 传统的进程间通信方式&#xff1a; 无名管道有名管道…