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.mzph.cn/news/94200.shtml

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

相关文章

软件测试基础学习

注意&#xff1a; 各位同学们&#xff0c;今年本人求职目前遇到的情况大体是这样了&#xff0c;开发太卷&#xff0c;学历高的话优势非常的大&#xff0c;公司会根据实际情况考虑是否值得培养&#xff08;哪怕技术差一点&#xff09;&#xff1b;学历稍微低一些但是技术熟练的…

Linux:minishell

目录 1.实现逻辑 2.代码及效果展示 1.打印字符串提示用户输入指令 2.父进程拆解指令 3.子进程执行指令,父进程等待结果 4.效果 3.实现过程中遇到的问题 1.打印字符串的时候不显示 2.多换了一行 3.cd路径无效 4.优化 1.ll指令 2.给文件或目录加上颜色 代码链接 模…

Scala第十三章节

Scala第十三章节 1. 高阶函数介绍 2. 作为值的函数 3. 匿名函数 4. 柯里化 5. 闭包 6. 控制抽象 7. 案例: 计算器 scala总目录 文档资料下载

mstsc无法保存RDP凭据, 100%生效

问题 即使如下两项都打勾&#xff0c;其还是无法保存凭据&#xff0c;特别是连接Ubuntu (freerdp server)&#xff1a; 解决方法 网上多种复杂方法&#xff0c;不生效&#xff0c;其思路是修改后台配置&#xff0c;以使mstsc跟平常一样自动记住凭据。最后&#xff0c;如下的…

CentOS7安装Oracle XE记录

本文仅是CentOS7安装Oracle XE记录&#xff0c;供参考 1、下载安装包 oracle-xe-11.2.0-1.0.x86_64.rpm.zip 2、安装 &#xff08;1&#xff09;第一次安装 [rootnode1 opt]# cd Disk1/ [rootnode1 Disk1]# ll 总用量 309884 -rw-r--r-- 1 root root 317320273 9月 28 09…

ARM---实现1-100求和任务

.text .globl _start_start:mov r0, #0x1mov r1, #0x1 给r1加一固定1不变mov r2, #0x64 100判断bl sumcmp r1, r2 sum:addcc r1, r1,#0x1 r1自增addcc r0, r0, r1 r0求和movcc pc,lrstop:b stop.end

day49数据库 索引 事务

一、索引 什么是索引&#xff1a;索引是数据库库中用来提高查询效率的技术&#xff0c;类似于目录 为什么要使用索引&#xff1a;如果不使用索引&#xff0c;数据会零散的保存在磁盘块中&#xff0c;查询数据需要遍历每一个磁盘块&#xff0c;直到找到数据为止&#xff0c;效率…

linux python 保存图形savefig import matplotlib.pyplot as plt

import matplotlib.pyplot as plt # 绘制图形 mod.plot_history(20)# 保存图形 plt.savefig("my_training_ephoes_plot.png") # 保存为PNG格式 # 保存图形并设置dpi参数 plt.savefig("my_plot.png", dpi600) # 保存为PNG格式&#xff0c;设置dpi为300

力扣 -- 115. 不同的子序列

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int numDistinct(string s, string t) {int ns.size();int mt.size();//多开一行&#xff0c;多开一列vector<vector<double>> dp(m1,vector<double>(n1));for(size_t j0;j<n;j){dp[…

C#餐饮收银系统

一、引言 餐饮收银系统是一种用于管理餐馆、咖啡厅、快餐店等餐饮业务的计算机化工具。它旨在简化点餐、结账、库存管理等任务&#xff0c;提高运营效率&#xff0c;增强客户体验&#xff0c;同时提供准确的财务记录。C# 餐饮收银系统是一种使用C#编程语言开发的餐饮业务管理软…

pytorch_神经网络构建1

文章目录 pytorch简介神经网络基础分类问题分析:逻辑回归模型逻辑回归实现多层神经网络多层网络搭建保存模型 pytorch简介 为什么神经网络要自定义数据类型torch.tensor? tensor可以放在gpu上训练,支持自动求导,方便快速训练,同时支持numpy的运算,是加强版,numpy不支持这些 为…

【网络通信三要素】TCP与UDP快速入门

网络通信三要素 1.什么是网络编程&#xff1f; 可以让设备中的程序&#xff0c;与网络上其他设备中的程序进行数据交互&#xff0c;从而实现网络通信的手段&#xff0c;java.net.*包下提供了网络编程的解决方案 2.基本的通信架构 基本的通信架构有2种形式&#xff1a;CS架构…

项目进展(五)-修复PCB电路板,学习32位ADC芯片ADS1285

一、前言 上个月29号放假了&#xff0c;和朋友一起去了南京(人是真滴多)&#xff0c;师兄晚放假几天&#xff0c;结果在测试时不小心把12V和GND碰触到一起了&#xff0c;导致12V短路&#xff0c;电路板几乎瘫痪了。 今天下午到学校之后就开始着手寻找问题和修复&#xff0c;最…

手机自动直播系统源码交付与代理加盟注意事项解析!

随着直播行业的不断发展&#xff0c;手机自动直播已经成为了人们生活中不可或缺的一部分。手机无人直播软件成了香饽饽&#xff0c;各类手机实景直播APP大批量涌现。因为创业和技术门槛低&#xff0c;市场需求高&#xff0c;所以成了最火热创业赛道。那么如果是不懂技术的人群&…

Matlab随机数的产生

目录 1、常见分布随机数的产生 1.1 二项分布 1.2 泊松分布 1.3 几何分布 1.4 均匀分布&#xff08;离散&#xff0c;等可能分布&#xff09; 1.5 均匀分布&#xff08;连续型等可能&#xff09; 1.6 指数分布&#xff08;描述“寿命”问题&#xff09; 1.7 正态分布 1.8…

SpringBoot结合Redisson实现分布式锁

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Sp…

C#,数值计算——Ranq2的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Backup generator if Ranq1 has too short a period and Ran is too slow.The /// period is 8.5E37. Calling conventions same as Ran, above. /// </summary> …

基于Matlab求解高教社杯全国大学生数学建模竞赛(CUMCM2004A题)-奥运会临时超市网点设计(附上源码+数据)

文章目录 题目思路源码数据下载 题目 2008年北京奥运会的建设工作已经进入全面设计和实施阶段。奥运会期间&#xff0c;在比赛主场馆的周边地区需要建设由小型商亭构建的临时商业网点&#xff0c;称为迷你超市&#xff08;Mini Supermarket, 以下记做MS&#xff09;网&#xf…

提升您的工作效率:TechSmith Snagit for Mac:强大的屏幕截图软件

在当今数字化的时代&#xff0c;屏幕截图已成为我们日常生活和工作中必不可少的一部分。无论是为了保存重要的信息、分享有趣的内容&#xff0c;还是为了制作教程和演示文稿&#xff0c;一款优秀的屏幕截图软件都能极大地提升我们的效率。而在所有的屏幕截图软件中&#xff0c;…

JavaScript:从入门到进阶的旅程

JavaScript是一种广泛使用的编程语言&#xff0c;为网页和应用程序提供了交互性和动态性。从初学者到资深开发者&#xff0c;JavaScript都是一项值得掌握的技能。在本文中&#xff0c;我们将探讨JavaScript的基础知识&#xff0c;以及一些进阶的概念和技巧。 一、JavaScript简…