C++算法 —— 动态规划(9)完全背包问题

文章目录

  • 1、动规思路简介
  • 2、完全背包【模板】
  • 3、零钱兑换
  • 4、零钱兑换Ⅱ
  • 5、完全平方数


背包问题需要读者先明白动态规划是什么,理解动规的思路,并不能给刚接触动规的人学习。所以最好是看了之前的动规博客,以及01背包博客,才能看完全背包博客,或者你本人就已经懂得动规了。

1、动规思路简介

动规的思路有五个步骤,且最好画图来理解细节,不要怕麻烦。当你开始画图,仔细阅读题时,学习中的沉浸感就体验到了。

状态表示
状态转移方程
初始化
填表顺序
返回值

动规一般会先创建一个数组,名字为dp,这个数组也叫dp表。通过一些操作,把dp表填满,其中一个值就是答案。dp数组的每一个元素都表明一种状态,我们的第一步就是先确定状态。

状态的确定可能通过题目要求来得知,可能通过经验 + 题目要求来得知,可能在分析过程中,发现的重复子问题来确定状态。还有别的方法来确定状态,但都大同小异,明白了动规,这些思路也会随之产生。状态的确定就是打算让dp[i]表示什么,这是最重要的一步。状态表示通常用某个位置为结尾或者起点来确定。

状态转移方程,就是dp[i]等于什么,状态转移方程就是什么。像斐波那契数列,dp[i] = dp[i - 1] + dp[i - 2]。这是最难的一步。一开始,可能状态表示不正确,但不要紧,大胆制定状态,如果没法推出转移方程,没法得到结果,那这个状态表示就是错误的。所以状态表示和状态转移方程是相辅相成的,可以帮你检查自己的思路。

要确定方程,就从最近的一步来划分问题。

初始化,就是要填表,保证其不越界。像第一段所说,动规就是要填表。比如斐波那契数列,如果要填dp[1],那么我们可能需要dp[0]和dp[-1],这就出现越界了,所以为了防止越界,一开始就固定好前两个值,那么第三个值就是前两个值之和,也不会出现越界。初始化的方式不止这一点,有些问题,假使一个位置是由前面2个位置得到的,我们初始化最一开始两个位置,然后写代码,会发现不够高效,这时候就需要设置一个虚拟节点,一维数组的话就是在数组0位置处左边再填一个位置,整个dp数组的元素个数也+1,让原先的dp[0]变为现在的dp[1],二维数组则是要填一列和一行,设置好这一行一列的所有值,原先数组的第一列第一行就可以通过新填的来初始化,这个初始化方法在下面的题解中慢慢领会。

第二种初始化方法的注意事项就是如何初始化虚拟节点的数值来保证填表的结果是正确的,以及新表和旧表的映射关系的维护,也就是下标的变化。

填表顺序。填当前状态的时候,所需要的状态应当已经计算过了。还是斐波那契数列,填dp[4]的时候,dp[3]和dp[2]应当都已经计算好了,那么dp[4]也就出来了,此时的顺序就是从左到右。还有别的顺序,要依据前面的分析来决定。

返回值,要看题目要求。

背包问题有很多种分类,此篇是关于完全背包问题的,优化方法写在模板题中,此后都直接写在代码上。

2、完全背包【模板】

DP42 【模板】完全背包

在这里插入图片描述
在这里插入图片描述

和01背包不同的是,完全背包一个数可以选择多次。dp[i][j]表示从前i个物品中选,总体积不超过j,所有选法中最大的价值。

最后一个位置i,如果不选,那就看dp[i - 1][j],如果选1个,那就看dp[i - 1][j - v[i]] + w[i],如果选2个,那就看dp[i - 1][j - 2v[i]] + 2w[i],依次类推,这里只有j和后面的w变了,那么再按照这个式子写出dp[i][j - v[i]]的值,最后通过数学计算能得到dp[i][j] = max(dp[i - 1][j], dp[i][j - v[i]] + w[i])。

初始化时,新增的一行一列全部为0。从上到下,从左到右填写,返回值是dp[n][V]。

接着第二问,再上面基础上做调整。 dp[i][j]表示体积必须等于j。按照之前的思路,dp[i][j] = -1来表示这个情况不存在,做不到要求。初始化时dp[0][0] = 0,第一行其余位置都是-1,第一列还是0。

#include <iostream>
#include <cstring>
using namespace std;const int N = 1010;int n, V, v[N], w[N];
int dp[N][N];int main()
{cin >> n >> V;for(int i = 1; i <= n; i++){cin >> v[i] >> w[i];}for(int i = 1; i <= n; i++){for(int j = 0; j <= V; j++){dp[i][j] = dp[i - 1][j];if(j >= v[i]) dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i]);}}cout << dp[n][V] << endl;memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; j++) dp[0][j] = -1;for(int i = 1; i <= n; i++){for(int j = 0; j <= V; j++){dp[i][j] = dp[i - 1][j];if(j >= v[i] && dp[i][j - v[i]] != -1)dp[i][j] = max(dp[i][j], dp[i][j - v[i]] + w[i]);}}cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl;return 0;
}

利用滚动数组做优化。和01背包不一样,它不需要从右到左来循环,它的一个位置需要同行的左边的一个位置和上方一个位置,同行这个位置得是更新后的。

#include <iostream>
#include <cstring>
using namespace std;const int N = 1010;int n, V, v[N], w[N];
int dp[N];int main()
{cin >> n >> V;for(int i = 1; i <= n; i++){cin >> v[i] >> w[i];}for(int i = 1; i <= n; i++){for(int j = v[i]; j <= V; j++){dp[j] = max(dp[j], dp[j - v[i]] + w[i]);}}cout << dp[V] << endl;memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; j++) dp[j] = -1;for(int i = 1; i <= n; i++){for(int j = v[i]; j <= V; j++){if(dp[j - v[i]] != -1)dp[j] = max(dp[j], dp[j - v[i]] + w[i]);}}cout << (dp[V] == -1 ? 0 : dp[V]) << endl;return 0;
}

还有一个优化

    memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; j++) dp[j] = -1;for(int i = 1; i <= n; i++){for(int j = v[i]; j <= V; j++){if(dp[j - v[i]] != -1)dp[j] = max(dp[j], dp[j - v[i]] + w[i]);}}cout << (dp[V] == -1 ? 0 : dp[V]) << endl;

这里有一个if判断,是为了能让这个dp值可用,再去让他+w[i]和dp[j]比较,我们可以让那个dp表中的每一个值足够小,这样即使dp[j - v[i]]参与了比较,也不会用它。

    memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; j++) dp[j] = -0x3f3f3f3f;//INT_MIN的一半,也足够小。for(int i = 1; i <= n; i++){for(int j = v[i]; j <= V; j++){dp[j] = max(dp[j], dp[j - v[i]] + w[i]);}}cout << (dp[V] < 0 ? 0 : dp[V]) << endl;

3、零钱兑换

322. 零钱兑换

在这里插入图片描述

dp[i][j]表示从前i个硬币中选,总金额正好等于j,所有的选法中,最少的硬币个数,j就是amount。

最后一个位置i,不选的话就看dp[i - 1][j]。如果选i,选1个,那么这个硬币价值就是coins[i],那为了能正好达到j而不超过,那就得看dp[i - 1][j - coins[i]],然后+1,因为存的是个数,如果选2个,那么就是dp[i - 1][j - 2coins[i]] + 2,根据之前的数学计算,选i的情况就是dp[i][j - coins[i]] + 1,然后和dp[i - 1][j]取小就行。

初始化时多加一行一列,dp[0][0]是0,第一行其它位置是无效的,因为没有硬币的话就不可能凑成金额,按照之前的优化,本来设置成-1然后判断,这里就初始化成足够大的数字就行,因为这道题就min,初始化成0x3f3f3f3f。

返回最后一个位置的值,但有可能到最后也凑不成,所以最后要判断一下。以及滚动数组优化。

    int coinChange(vector<int>& coins, int amount) {const int INF = 0x3f3f3f3f;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];}

4、零钱兑换Ⅱ

518. 零钱兑换 II

在这里插入图片描述

看了上一个题的思路,这题很快就出来答案了。

上一个题代码

    int coinChange(vector<int>& coins, int amount) {const int INF = 0x3f3f3f3f;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];}

现在要求组合数,所以选i的情况中就不需要+1了。不用求min,而是所有可能的数值加起来,按照优化后的代码,dp[j] = dp[j] + dp[j - coins[i]],返回时不需要判断,直接返回最后一个位置的值即可。

    int change(int amount, vector<int>& coins) {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];}

5、完全平方数

279. 完全平方数

在这里插入图片描述

也是一个完全背包问题,dp[i][j]表示从前i个完全平方数中挑选,总和正好等于j,所有选法中,最小的数量。

从1到i方的区间来分析,不选i方,那就看dp[i - 1][j],选一个i方,那就看dp[i - 1][j - i^2] + 1,选两个i方,和之前的一样,最后就是dp[i][j - i^2] + 1,然后两个数取min。

初始化,dp[0][0]是0,第一行其余位置都是不存在的,所以也弄成特殊值0x3f3f3f3f,第一列不用管,默认为0就行。

返回值是dp[根号n][n]。

    int numSquares(int 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.rhkb.cn/news/148118.html

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

相关文章

项目测试练习

项目背景项目功能测试计划Bug总结升级自动化测试正常登录流程 项目背景 1&#xff1a;博客之站系统是采用前后端分离的方式来实现&#xff1b;使用MySQL、Redis数据库储存相关数据&#xff1b;同时部署到云服务器上。 2&#xff1a;包含注册页、登录页、博客列表页、个人列表页…

前端Vue框架系列—— 学习笔记总结Day01

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

双重差分模型(DID)论文写作指南与操作手册

手册链接&#xff1a;双重差分模型&#xff08;DID&#xff09;论文写作指南与操作手册https://www.cctalk.com/m/group/90983583?xh_fshareuid60953990 简介&#xff1a; 当前&#xff0c;对于准应届生们来说&#xff0c;毕设季叠加就业季&#xff0c;写作时间显得十分宝贵…

Django基础讲解-路由控制器和视图(Django-02)

一 路由控制器 参考链接&#xff1a; Django源码阅读&#xff1a;路由&#xff08;二&#xff09; - 知乎 Route路由, 是一种映射关系&#xff01;路由是把客户端请求的 url路径与视图进行绑定 映射的一种关系。 这个/timer通过路由控制器最终匹配到myapp.views中的视图函数 …

监狱劳动工具管理系统|智工具DW-S308的功能

监狱劳动工具管理系统(智工具DW-S308)是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对工具进行统一管理、分析的信息化、智能化、规范化的系统。 目前监狱的劳动工具管理很多还停留在固定工位&#xff0c;人盯人、人管人等落后的管理模式&#xff…

代码随想录 Day10 栈与队列 LeetCode T239 滑动窗口的最大值 T347 前K个高频元素

简要介绍一下单调队列和优先级队列的不同 元素顺序的处理&#xff1a;单调队列中&#xff0c;元素的顺序是单调的&#xff0c;也就是说&#xff0c;队列中的元素按照特定的单调性&#xff08;递增或递减&#xff09;排列。这种特性使得单调队列在处理一些问题时非常高效&#…

postgresql-聚合函数增强功能

postgresql-聚合函数增强功能 按季度统计入职员工 按季度统计入职员工 select -- extract截取&#xff0c;按季度进行统计入职员工总数 extract(year from hire_date), count(*) filter(where extract(quarter from hire_date) 1) "第一季度", count(*) filter(wh…

Meta分析如何下笔?掌握这些干货就够了

Meta分析是针对某一科研问题&#xff0c;根据明确的搜索策略、选择筛选文献标准、采用严格的评价方法&#xff0c;对来源不同的研究成果进行收集、合并及定量统计分析的方法&#xff0c;最早出现于“循证医学”&#xff0c;现已广泛应用于农林生态&#xff0c;资源环境等方面。…

【网络】网络扫盲篇 ——用简单语言和图解带你入门网络

网络的一些名词和基础知识讲解 前言正式开始一些基础知识发展背景运营商和生产商 协议协议的分层TCP/IP五层(或四层)模型&#xff08;可以不看&#xff0c;对新手来说太痛苦了&#xff0c;我这里只是为了让屏幕前的你过一遍就好&#xff0c;里面很多概念新手是不太懂的&#xf…

Nginx在CentOS上的安装部署、RabbitMQ在CentOS上安装部署

目录 1. Nginx在CentOS上的安装部署 1.1 Nginx简介 1.2 Nginx安装 1.2.1 安装yum依赖程序 1.2.2 手动添加&#xff0c;nginx的yum仓库 1.2.3 通过yum安装最新稳定版的nginx 1.2.4 启动 1.2.5 配置防火墙放行 1.2.6 启动后浏览器输入Linux服务器的IP地址或主机…

JavaSE入门--初始Java

文章目录 Java语言概述认识Java的main函数main函数示例运行Java程序认识注释认识标识符认识关键字 前言&#xff1a; 我从今天开始步入Java的学习&#xff0c;希望自己的博客可以带动小白学习&#xff0c;也能获得大佬的指点&#xff0c;日后能互相学习进步&#xff0c;都能如尝…

gif怎么转换成视频MP4?

gif怎么转换成视频MP4&#xff1f;GIF动图已成为一种风靡网络的流行的特殊图片文件&#xff0c;其循环播放和逐帧呈现的特点使其在社交媒体、聊天应用等场合广泛应用&#xff0c;平时我们进行群聊是&#xff0c;大家总会一些gif动态表情的出现而感觉非常的开行&#xff0c;gif动…

【做题笔记】多项式/FFT/NTT

HDU1402 - A * B Problem Plus 题目链接 大数乘法是多项式的基础应用&#xff0c;其原理是将多项式 f ( x ) a 0 a 1 x a 2 x 2 a 3 x 3 ⋯ a n x n f(x)a_0a_1xa_2x^2a_3x^3\cdotsa_nx^n f(x)a0​a1​xa2​x2a3​x3⋯an​xn中的 x 10 x10 x10&#xff0c;然后让大数的…

Docker-mysql,redis安装

安装MySQL 下载MySQL镜像 终端运行命令 docker pull mysql:8.0.29镜像下载完成后&#xff0c;需要配置持久化数据到本地 这是mysql的配置文件和存储数据用的目录 切换到终端&#xff0c;输入命令&#xff0c;第一次启动MySQL容器 docker run --restartalways --name mysq…

JWFD开源工作流大模型设计器

JWFD开源工作流大模型设计器&#xff0c;把流程图的拓扑结构从几十个节点扩展到数千个节点&#xff0c;就不是使用绘图器的模式了&#xff0c;需要开发一个模型生成器了&#xff0c;尝试做一下大模型&#xff0c;赶赶时髦啊

【算法导论】线性时间排序(计数排序、基数排序、桶排序)

引言&#xff1a;   在排序的最终结果中&#xff0c;各元素的次序依赖于它们之间的比较&#xff0c;我们把这类排序算法称为比较排序&#xff0c;对于包含n个元素的输入序列来说&#xff0c;任何比较排序在最坏情况下都要经过 Ω ( n l g n ) \Omega(nlgn) Ω(nlgn)次比较&a…

希尔排序(C++实现)

文章目录 前言1. 基础概念2. 动图演示3. 代码实现4. 排序过程5. 效率分析6. 总结 前言 上篇文章讲了直接插入排序算法。 首先&#xff0c;在待排序的数组中&#xff0c;元素本身就是有序的情况下&#xff0c;就不需要移动任何元素&#xff0c;所以直接插入排序最好情况时间复…

使用win_b64做CATIA的开发测试

文章目录 一、把win_b64文件夹放在无中文的路径下二、启动环境文件编辑器三、新建环境文件四、集成CAA代码五、去用户环境目录查看环境文件是否生成六、关闭环境文件编辑器七、去桌面双击新建的快捷方式即可启动CATIA&#xff0c;进行测试八、进入对应的开发模块&#xff0c;查…

第十二届2023软件杯国家二等奖赛后感想总结

一&#xff0c;相关链接 软件杯官网&#xff1a;软件杯大赛官网 (cnsoftbei.com) 金蝶赛道&#xff1a;金蝶云苍穹开发者门户 (kingdee.com) 二&#xff0c;个人介绍 首先我是个双非院校的学生&#xff0c;专业为计算机科学与技术&#xff0c;打这个比赛是在大二下的暑假开始的…

想要精通算法和SQL的成长之路 - 二叉树的序列化和反序列化问题

想要精通算法和SQL的成长之路 - 二叉树的序列化和反序列化问题 前言一. 二叉树的层序遍历&#xff08;BFS&#xff09;二. 二叉树的序列化与反序列化2.1 序列化操作2.2 反序列化操作 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 二叉树的层序遍历&#xff08;BFS&#xf…