【动态规划】风扫枯杨,满地堆黄叶 - 9. 完全背包问题

在这里插入图片描述

本篇博客给大家带来的是完全背包问题之动态规划解法技巧.
🐎文章专栏: 动态规划
🚀若有问题 评论区见
欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

王子,公主请阅🚀

  • 要开心
    • 要快乐
      • 顺便进步
  • 1. 完全背包
  • 2. 零钱兑换

要开心

要快乐

顺便进步

1. 完全背包

题目链接: DP42 【模板】完全背包

题目内容:
描述
你有一个背包,最多能容纳的体积是V。

现在有n种物品,每种物品有任意多个,第i种物品的体积为
vi ,价值为wi 。

(1)求这个背包至多能装多大价值的物品?
(2)若背包恰好装满,求至多能装多大价值的物品?
输入描述:
第一行两个整数n和V,表示物品个数和背包体积。
接下来n行,每行两个数

vi 和 wi,表示第i种物品的体积和价值。
1≤n,V≤1000

输出描述:
输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。

解题须知:
完全背包问题与01背包问题的区别:
01背包问题中一种物品只能选一个.
完全背包问题种一种物品能选多个.

第一 先解决第一问

1. 状态表示
dp[i][j]表示从前 i 个物品中选,总体积不超过 j,所有选法中能选出的最大价值.

2. 状态转移方程
与01背包问题一样,
根据最后一个物品的情况来划分问题:
在这里插入图片描述
最后一个物品不选:dp[i][j] = dp[i-1][j];
选一个: dp[i][j] = dp[i-1][j-v[i]] + w[i];
选两个: dp[i][j] = dp[i-1][j-2×v[i]] + 2×w[i];

选k个: dp[i][j] = dp[i-1][j-k×v[i]] + k×w[i];

上述多种情况求最大值:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-v[i]]+w[i],dp[i-1][j-2×v[i]] + 2×w[i],…,dp[i-1][j-k×v[i]] + k×w[i]); ①
dp[i][j-v[i]] + w[i] = max(dp[i-1][j-v[i]]+w[i],dp[i-1][j-2×v[i]] + 2×w[i],…,dp[i-1][j-h×v[i]] + h×w[i]); ②

先说结论: ①式中的k 一定等于②中的h.这是为什么呢?
在状态表示中,我们定义dp[i][j]表示从前 i 个物品中选,总体积不超过 j ,所有选法中能选出的最大价值.
随着所选物品越多, j 一定会趋于0, 无论是①还是②都一定是这样的, 所以k = h;
状态转移方程只能是有限个递推公式,所以需要化简上述式子,那么由①和②可得:
dp[i][j] = max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
dp[i][j-v[i]]+w[i]式子需要保证 j >= v[i]

3. 初始化
多创建一行一列,处理两个细节:
Ⅰdp表与原数组的下标对应关系:
不做任何处理时是: i – i-1
但此题, 读入有效元素是从下标1开始的,
所以 i – i
Ⅱ初始化虚拟节点:
第一行,根据定义 当 i = 0时,没有物品,无论怎么选最大价值都是0.
第一列(除第一个位置)无需初始化, 因为 j >= v[i] 只有dp[0][0]满足.
在这里插入图片描述

4. 填表顺序
看状态转移方程,
要想得到dp[i][j] 就得知道dp[i-1][j]和dp[i][j-v[i]]+w[i];
所以从上往下填写每一行
每一行从左往右填写.

5. 返回值
根据状态表示和题目要求
打印 dp[n][V]即可.

6. 优化
在这里插入图片描述

第二 解决第二问

1. 状态表示
dp[i][j]表示从前 i 个物品中选,总体积等于 j,所有选法中能选出的最大价值.

2. 状态转移方程

根据最后一个物品的情况来划分问题:
在这里插入图片描述
最后一个物品不选:dp[i][j] = dp[i-1][j];
选一个: dp[i][j] = dp[i-1][j-v[i]] + w[i];
选两个: dp[i][j] = dp[i-1][j-2×v[i]] + 2×w[i];

选k个: dp[i][j] = dp[i-1][j-k×v[i]] + k×w[i];

上述多种情况求最大值:
dp[i][j] = max(dp[i-1][j],dp[i-1][j-v[i]]+w[i],dp[i-1][j-2×v[i]] + 2×w[i],…,dp[i-1][j-k×v[i]] + k×w[i]); ①
dp[i][j-v[i]] + w[i] = max(dp[i-1][j-v[i]]+w[i],dp[i-1][j-2×v[i]] + 2×w[i],…,dp[i-1][j-h×v[i]] + h×w[i]); ②

先说结论: ①式中的k 一定等于②中的h.这是为什么呢?
在状态表示中,我们定义dp[i][j]表示从前 i 个物品中选,总体积不超过 j ,所有选法中能选出的最大价值.
随着所选物品越多, j 一定会趋于0, 无论是①还是②都一定是这样的, 所以k = h;
状态转移方程只能是有限个递推公式,所以需要化简上述式子,那么由①和②可得:
dp[i][j] = max(dp[i-1][j],dp[i][j-v[i]]+w[i]);
dp[i][j-v[i]]+w[i]式子需要保证 j >= v[i]

第二问需要多考虑一个细节, 所选择的 i 物品并不一定能够保证 j-v[i] 恰好等于0, 有可能背包体积有剩余.
当背包体积有剩余时,规定dp[i][j-v[i]] = -1;
于是需要满足条件:
j - v[i] >= 0 && dp[i][j-v[i]] != -1;
3. 初始化
多创建一行一列,处理两个细节:
Ⅰdp表与原数组的下标对应关系:
不做任何处理时是: i – i-1
但此题, 读入有效元素是从下标1开始的,
所以 i – i
Ⅱ初始化虚拟节点:
第一行,根据定义 当 i = 0且j >= 1时,没有物品可选, 意味着背包体积有剩余.故dp[0][j] = -1;
第一列(除第一个位置)无需初始化, 因为 j >= v[i] 只有dp[0][0]满足.
在这里插入图片描述

4. 填表顺序
看状态转移方程,
要想得到dp[i][j] 就得知道dp[i-1][j]和dp[i][j-v[i]]+w[i];
所以从上往下填写每一行
每一行从左往右填写.

5. 返回值
根据状态表示和题目要求
打印 dp[n][V]即可.

6. 优化
在这里插入图片描述

第三 代码实现

//优化前:// Scanner in = new Scanner(System.in);// // 注意 hasNext 和 hasNextLine 的区别// int N = 1010;// int[][] dp = new int[N][N];// int[][] dp2 = new int[N][N];// int[] v = new int[N];// int[] w = new int[N];// int n = in.nextInt();// int V = in.nextInt();// for(int i = 1;i <= n;i++) {//     v[i] = in.nextInt();//     w[i] = in.nextInt();// }// //解决第一问// for(int i = 1;i <= n;++i) {//     for(int j = 0;j <= V;++j) {//j需要从0开始,因为初始化的时候并没有考虑第一列的全部位置,只考虑了第一列的第一个位置.//         dp[i][j] = dp[i-1][j];//         if(j >= v[i]) {//             dp[i][j] = Math.max(dp[i][j],dp[i][j-v[i]]+w[i]);//         }//     }// }// System.out.println(dp[n][V]);// //解决第二问// for(int i = 1;i <= V;++i) {//     dp2[0][i] = -1;// }// for(int i = 1;i <= n;++i) {//     for(int j = 0;j <= V;++j) {//j需要从0开始,因为初始化的时候并没有考虑第一列的全部位置,只考虑了第一列的第一个位置.//         dp2[i][j] = dp2[i-1][j];//         if(j >= v[i] && dp2[i][j-v[i]] != -1) {//             dp2[i][j] = Math.max(dp2[i][j],dp2[i][j-v[i]]+w[i]);//         }//     }// }// System.out.println(dp2[n][V] == -1 ? 0 : dp2[n][V]);//优化后:Scanner in = new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别int N = 1010;int[] dp = new int[N];int[] dp2 = new int[N];int[] v = new int[N];int[] w = new int[N];int n = in.nextInt();int V = in.nextInt();for(int i = 1;i <= n;i++) {v[i] = in.nextInt();w[i] = in.nextInt();}//解决第一问for(int i = 1;i <= n;++i) {for(int j = 0;j <= V;++j) {//j需要从0开始,因为初始化的时候并没有考虑第一列的全部位置,只考虑了第一列的第一个位置.if(j >= v[i]) {dp[j] = Math.max(dp[j],dp[j-v[i]]+w[i]);}}}System.out.println(dp[V]);//解决第二问for(int i = 1;i <= V;++i) {dp2[i] = -1;}for(int i = 1;i <= n;++i) {for(int j = 0;j <= V;++j) {//j需要从0开始,因为初始化的时候并没有考虑第一列的全部位置,只考虑了第一列的第一个位置.dp2[j] = dp2[j];if(j >= v[i] && dp2[j-v[i]] != -1) {dp2[j] = Math.max(dp2[j],dp2[j-v[i]]+w[i]);}}}System.out.println(dp2[V] == -1 ? 0 : dp2[V]);}
}

2. 零钱兑换

题目链接: 322. 零钱兑换

题目内容:
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:

输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:

输入:coins = [2], amount = 3
输出:-1
示例 3:

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

第一 动态规划

1. 状态表示
dp[i][j]表示从前 i 个硬币中选,总金额等于 j,所有选法中能选出的最少硬币个数.

2. 状态转移方程

根据最后一个物品的情况来划分问题:
在这里插入图片描述
最后一个物品不选:dp[i][j] = dp[i-1][j];
选一个: dp[i][j] = dp[i-1][j-coins[i]] + 1;
选两个: dp[i][j] = dp[i-1][j-2×coins[i]] + 2;

选k个: dp[i][j] = dp[i-1][j-k×coins[i]] + k;

上述多种情况求最小值:
dp[i][j] = min(dp[i-1][j],dp[i-1][j-coins[i]]+1,dp[i-1][j-2×coins[i]] + 2,…,dp[i-1][j-k×coins[i]] + k); ①
dp[i][j-v[i]] + 1 = min(dp[i-1][j-coins[i]]+1,dp[i-1][j-2×coins[i]] + 2,…,dp[i-1][j-h×coins[i]] + h); ②

先说结论: ①式中的k 一定等于②中的h.这是为什么呢?
在状态表示中,我们定义dp[i][j]表示从前 i 个硬币中选,总金额不超过 j ,所有选法中能选出的最少硬币个数.
随着所选硬币越多, j 一定会趋于0, 无论是①还是②都一定是这样的, 所以k = h;
状态转移方程只能是有限个递推公式,所以需要化简上述式子,那么由①和②可得:
dp[i][j] = max(dp[i-1][j],dp[i][j-coins[i]]+1);
dp[i][j-coins[i]]+1式子需要保证 j >= v[i]

还需要多考虑一个细节, 所选择的 i 硬币并不一定能够保证 j-coins[i] 恰好等于0, 有可能背包有剩余.
当背包有剩余时,规定dp[i][j-硬币[i]] = 0x3f3f3f3f;
于是需要满足条件:
j - coins[i] >= 0 && dp[i][j-coins[i]] != 0x3f3f3f3f;
3. 初始化
多创建一行一列,处理两个细节:
Ⅰdp表与原数组的下标对应关系:
i – i-1
Ⅱ初始化虚拟节点:
第一行,根据定义 当 i = 0且j >= 1时,没有硬币可选, 意味着背包有剩余.故dp[0][j] = 0x3f3f3f3f;
第一列(除第一个位置)无需初始化, 因为 j >= coins[i] 只有dp[0][0]满足.
在这里插入图片描述

4. 填表顺序
看状态转移方程,
要想得到dp[i][j] 就得知道dp[i-1][j]和dp[i][j-coins[i]]+1;
所以从上往下填写每一行
每一行从左往右填写.

5. 返回值
根据状态表示和题目要求
return dp[coins.length][amount]即可.

6. 优化
在这里插入图片描述

第二 代码实现

class Solution {public int coinChange(int[] coins, int amount) {//优化前:// int n = coins.length;// int[][] dp = new int[n+1][amount+1];// //2.初始化// for(int i = 1;i <= amount;++i) {//     dp[0][i] = Integer.MAX_VALUE;// }  // //3.填表// for(int i = 1;i <= n;++i) {//     for(int j = 0;j <= amount;++j) {//j需要从0开始,因为初始化的时候并没有考虑第一列的全部位置,只考虑了第一列的第一个位置.//         dp[i][j] = dp[i-1][j];//         if(j >= coins[i-1] && dp[i][j-coins[i-1]] != Integer.MAX_VALUE) {//             dp[i][j] = Math.min(dp[i][j],dp[i][j-coins[i-1]]+1);//         }//     }// }// return dp[n][amount] == Integer.MAX_VALUE ? -1 : dp[n][amount];//优化后:int n = coins.length;int[] dp = new int[amount+1];//2.初始化for(int i = 1;i <= amount;++i) {dp[i] = 0x3f3f3f3f;}  //3.填表for(int i = 1;i <= n;++i) {for(int j = 0;j <= amount;++j) {//j需要从0开始,因为初始化的时候并没有考虑第一列的全部位置,只考虑了第一列的第一个位置.if(j >= coins[i-1] && dp[j-coins[i-1]] != 0x3f3f3f3f) {dp[j] = Math.min(dp[j],dp[j-coins[i-1]]+1);}}}return dp[amount] == 0x3f3f3f3f ? -1 : dp[amount];}
}

本篇博客到这里就结束啦, 感谢观看 ❤❤❤

🐎期待与你的下一次相遇😊😊😊

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

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

相关文章

MyBatis的工作流程是怎样的?

大家好&#xff0c;我是锋哥。今天分享关于【MyBatis的工作流程是怎样的&#xff1f;】面试题。希望对大家有帮助&#xff1b; MyBatis的工作流程是怎样的&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MyBatis 的工作流程可以分为几个主要的步骤&…

python-leetcode 25.环形链表

题目&#xff1a; 给定一个链表的头节点head,判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪next指针再次到达&#xff0c;则链表中存在环。为了表示给定链表中的环&#xff0c;评测系统内部使用整数pos来表示链表尾连接到链表中的位置&#xff08;…

瑞芯微开发板/主板Android调试串口配置为普通串口方法 深圳触觉智能科技分享

本文介绍瑞芯微开发板/主板Android调试串口配置为普通串口方法&#xff0c;不同板型找到对应文件修改&#xff0c;修改的方法相通。触觉智能RK3562开发板演示&#xff0c;搭载4核A53处理器&#xff0c;主频高达2.0GHz&#xff1b;内置独立1Tops算力NPU&#xff0c;可应用于物联…

Datawhale 组队学习 Ollama教程 task1

一、Ollama 简介 比喻&#xff1a;Ollama 就像是一个“魔法箱子”&#xff0c;里面装满了各种大型语言模型&#xff08;LLM&#xff09;。你不需要懂复杂的魔法咒语&#xff08;配置&#xff09;&#xff0c;只需要轻轻一按&#xff08;一条命令&#xff09;&#xff0c;就能让…

vulnhub 靶场 —— NullByte

免责声明 本博客文章仅供教育和研究目的使用。本文中提到的所有信息和技术均基于公开来源和合法获取的知识。本文不鼓励或支持任何非法活动&#xff0c;包括但不限于未经授权访问计算机系统、网络或数据。 作者对于读者使用本文中的信息所导致的任何直接或间接后果不承担任何…

使用 meshgrid函数绘制网格点坐标的原理与代码实现

使用 meshgrid 绘制网格点坐标的原理与代码实现 在 MATLAB 中&#xff0c;meshgrid 是一个常用函数&#xff0c;用于生成二维平面网格点的坐标矩阵。本文将详细介绍如何利用 meshgrid 函数生成的矩阵绘制网格点的坐标&#xff0c;并给出具体的代码实现和原理解析。 实现思路 …

【STM32系列】利用MATLAB配合ARM-DSP库设计FIR数字滤波器(保姆级教程)

ps.源码放在最后面 设计IIR数字滤波器可以看这里&#xff1a;利用MATLAB配合ARM-DSP库设计IIR数字滤波器&#xff08;保姆级教程&#xff09; 前言 本篇文章将介绍如何利用MATLAB与STM32的ARM-DSP库相结合&#xff0c;简明易懂地实现FIR低通滤波器的设计与应用。文章重点不在…

使用mermaid画流程图

本文介绍使用mermaid画流程图&#xff0c;并给出几个示例。 背景 目前&#xff0c;除有明确格式要求的文档外&#xff0c;笔者一般使用markdown写文档、笔记。当文档有图片时&#xff0c;使用Typora等软件可实时渲染&#xff0c;所见即所得。但如果文档接收方没有安装相关工具…

12.项目结构

后端结构 ruoyi-admin 项目启动的入口 提供了两种启动方式 1.RuoYiApplication基于springboot,内置tomcat,直接运行。 2.RuoYiServletInitializer将springboot项目打成一个war包,用外置的servlet容器来运行。 通用功能的controller 后台登录相关的、权限控制相关的、数据字…

基于springboot+vue的游戏创意工坊与推广平台的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

25自动化考研复试面试常见核心问题真题汇总,自动化考研复试面试有哪些经典问题?自动化考研复试难不难啊?

你是不是正在为考研自动化专业的复试发愁&#xff1f;担心准备不充分、表现不好&#xff1f;别慌&#xff01;今天&#xff0c;学姐——复试面试拿下90分、成功上岸的学姐&#xff0c;来给大家分享备考秘诀。复试没那么可怕&#xff0c;只要掌握正确的方法&#xff0c;你也可以…

【HarmonyOS Next 自定义可拖拽image】

效果图&#xff1a; 代码&#xff1a; import display from "ohos.display" import { AppUtil } from "pura/harmony-utils"/*** 自定义可拖拽图标组件*/ Component export default struct DraggableImage {imageResource?: ResourceimageHeight: numbe…

从0搭建卷积神经网络(CNN)--详细教学

目录 一、卷积神经网络介绍 1、简介 经典CNN架构 2、与传统神经网络区别 3、卷积神经网络的结构 (1) 卷积层&#xff08;Convolutional Layer&#xff09; (2) 激活函数&#xff08;Activation Function&#xff09; (3) 池化层&#xff08;Pooling Layer&#xff09; …

Jmeter对图片验证码的处理

Jmeter对图片验证码的处理 在web端的登录接口经常会有图片验证码的输入&#xff0c;而且每次登录时图片验证码都是随机的&#xff1b;当通过jmeter做接口登录的时候要对图片验证码进行识别出图片中的字段&#xff0c;然后再登录接口中使用&#xff1b; 通过jmeter对图片验证码…

深入理解指针初阶:从概念到实践

一、引言 在 C 语言的学习旅程中&#xff0c;指针无疑是一座必须翻越的高峰。它强大而灵活&#xff0c;掌握指针&#xff0c;能让我们更高效地操作内存&#xff0c;编写出更优化的代码。但指针也常常让初学者望而生畏&#xff0c;觉得它复杂难懂。别担心&#xff0c;本文将用通…

【CubeMX-HAL库】STM32F407—无刷电机学习笔记

目录 简介&#xff1a; 学习资料&#xff1a; 跳转目录&#xff1a; 一、工程创建 二、板载LED 三、用户按键 四、蜂鸣器 1.完整IO控制代码 五、TFT彩屏驱动 六、ADC多通道 1.通道确认 2.CubeMX配置 ①开启对应的ADC通道 ②选择规则组通道 ③开启DMA ④开启ADC…

java配置api,vue网页调用api从oracle数据库读取数据

一、主入口文件 1&#xff1a;java后端端口号 2&#xff1a;数据库类型 和 数据库所在服务器ip地址 3&#xff1a;服务器用户名和密码 二、映射数据库表中的数据 resources/mapper/.xml文件 1&#xff1a;column后变量名是数据库中存储的变量名 property的值是column值的…

Python——批量图片转PDF(GUI版本)

目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码总结专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️‍🌈 博客主页:请点击——> 一晌小贪欢的博客主页求关注 👍 该系列文章专栏:请点击——>Python办公自动化专…

Photoshop自定义键盘快捷键

编辑 - 键盘快捷键 CtrlShiftAltK 把画笔工具改成Q , 橡皮擦改成W , 涂抹工具改成E , 增加和减小画笔大小A和S 偏好设置 - 透明度和色域 设置一样颜色 套索工具 可以自定义套选一片区域 Shiftf5 填充 CtrlU 可以改颜色/色相/饱和度 CtrlE 合并图层 CtrlShiftS 另存…

C++ 学习:深入理解 Linux 系统中的冯诺依曼架构

一、引言 冯诺依曼架构是现代计算机系统的基础&#xff0c;它的提出为计算机的发展奠定了理论基础。在学习 C 和 Linux 系统时&#xff0c;理解冯诺依曼架构有助于我们更好地理解程序是如何在计算机中运行的&#xff0c;包括程序的存储、执行和资源管理。这对于编写高效、可靠…