【动态规划】(三)动态规划——完全背包

动态规划——完全背包

  • 完全背包理论基础
  • 零钱兑换Ⅱ
  • 组合总和Ⅳ
  • 爬楼梯(进阶版)
  • 零钱兑换
  • 完全平方数
  • 单词拆分
  • 背包问题总结

完全背包理论基础

  有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。

重量价值
物品0115
物品1320
物品2430

回顾一下01背包的核心代码:

for(int i = 0; i < weight.size(); i++) { // 遍历物品for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}
}

我们知道01背包内嵌的循环是从大到小遍历,为了保证每个物品仅被添加一次。

而完全背包的物品是可以添加多次的,所以要从小到大去遍历,即:

// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) { // 遍历物品for(int j = weight[i]; j <= bagWeight ; j++) { // 遍历背包容量dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}
}

其实还有一个很重要的问题,在纯完全背包中,对于一维dp数组来说,其实两个for循环嵌套顺序是无所谓的! 但在应用中稍有变形则另说。

因为dp[j] 是根据 下标 j 之前所对应的dp[j]计算出来的。 只要保证下标j之前的dp[j]都是经过计算的就可以了。

遍历物品在外层循环,遍历背包容量在内层循环,状态如图:

遍历背包容量在外层循环,遍历物品在内层循环,状态如图:


但在实际应用过程中:

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

// 先遍历物品,再遍历背包
for(int i = 0; i < weight.size(); i++) 
{ // 遍历背包容量for(int j = weight[i]; j <= bagWeight ; j++) { 递推公式}
}

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

// 先遍历背包,再遍历物品
for(int j = 0; j <= bagWeight; j++) 
{// 遍历物品for(int i = 0; i < weight.size(); i++) {递推公式}
}

零钱兑换Ⅱ

力扣链接

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

示例:
输入: amount = 5, coins = [1, 2, 5]
输出: 4

注意

  • 0 <= amount (总金额) <= 5000
  • 1 <= coin (硬币面额) <= 5000
  • 硬币种类不超过 500 种
  • 结果符合 32 位符号整数

本题为完全背包,装满背包的组合数

动态规划五部曲:

  • dp[j]含义: 凑成总金额j的货币组合数为dp[j]
  • 确定递推公式: dp[j] += dp[j - coins[i]];
  • dp数组初始化: dp[0] = 1(递归的基础), 其他为 0 (累加的初值)
  • 确定遍历顺序: 组合 先物品后背包,顺序遍历
  • 举例推导dp数组: 输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:

程序实现:

int change(int amount, vector<int>& coins) 
{// dp[i] :  可凑成总金额为 i 的组合有 dp[i] 种 最终求dp[amount]vector<int> dp(amount + 1, 0);// 物品重量 coins[i]  物品价值 coins[i]// 背包大小 amountdp[0] = 1;// 遍历物品for(int  i = 0; i < coins.size(); i++){// 遍历背包 组合问题for(int j = coins[i]; j <= amount; j++){dp[j] += dp[j - coins[i]];}}return dp[amount];
}

组合总和Ⅳ

力扣原题

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

示例:
nums = [1, 2, 3]
target = 4
所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1)

请注意,顺序不同的序列被视作不同的组合。

因此输出为 7

完全背包,求不同的排列数。

因此本题只需在上题的基础上修改遍历顺序即可,对于求排列数,先遍历背包,后遍历物品。

举例来推导dp数组

程序实现:

int combinationSum4(vector<int>& nums, int target) 
{// dp[i] :  组成目标数 i 的组合有 dp[i] 个vector<int> dp(target + 1, 0);// 物品重量 nums[i]  物品价值 nums[i] // 背包大小 targetdp[0] = 1;// 遍历背包for(int j = 0; j <= target; j++){// 遍历物品for(int  i = 0; i < nums.size(); i++){if(j - nums[i] >= 0)dp[j] += dp[j - nums[i]];}}return dp[target];
}

爬楼梯(进阶版)

对基础题的爬楼梯进行修改: 改变每次可爬的最大步数

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?

动态规划五部曲:

  • dp[j]含义: 爬到有i个台阶的楼顶,有dp[i]种方法。
  • 确定递推公式: dp[i]有几种来源,dp[i - 1],dp[i - 2],dp[i - 3] 等等,即:dp[i - j],那么递推公式为:
    dp[i] += dp[i - j]
    
  • dp数组初始化: dp[0] = 1,是递归的基础,其他为0,累加的初始化
  • 确定遍历顺序: 这里属于背包的排序问题,因为1、2 步 和 2、1 步都是上三个台阶,但是这两种方法不一样! 所以需要先遍历背包,后物品:
     // 遍历背包
    for (int i = 1; i <= n; i++) 
    {// 遍历物品for (int j = 1; j <= m; j++) {if (i - j >= 0) dp[i] += dp[i - j];}
    }
    

零钱兑换

力扣链接

  给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

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

完全背包,装满背包所花的物品最少个数

动态规划五部曲:

  • dp[j]含义: 凑足总额为j所需钱币的最少个数为dp[j]

  • 确定递推公式: 凑足总额为j - coins[i]的最少个数为dp[j - coins[i]],那么只需要加上一个钱币coins[i]即dp[j - coins[i]] + 1就是dp[j],所以dp[j] 要取所有 dp[j - coins[i]] + 1 中最小的:

    dp[j] = min(dp[j], dp[j-coins[i]] + 1);
    
  • dp数组初始化: dp[0] = 1, 其他为 INT_MAX,防止在取 min 时覆盖实际值

  • 确定遍历顺序: 因为本题为求钱币的个数,无论是排列思想还是组合思想都不影响个数,因为遍历前后无关,无论哪个先遍历都可以的。

  • 举例推导dp数组: 以输入:coins = [1, 2, 5], amount = 5为例:

程序实现:

int coinChange(vector<int>& coins, int amount) 
{// dp[i] :  可凑成总金额为 i 的最少硬币数 最终求dp[amount]vector<int> dp(amount + 1, INT_MAX);// 物品重量 coins[i]  物品价值 coins[i]// 背包大小 amountdp[0] = 0;// 遍历物品for(int  i = 0; i < coins.size(); i++){// 遍历背包for(int j = coins[i]; j <= amount; j++){	if(dp[j - coins[i]] != INT_MAX)dp[j] = min(dp[j - coins[i]]+1,dp[j]);}}for(int num:dp)cout << num << " ";if(dp[amount] == INT_MAX)return -1;return dp[amount];
}

完全平方数

  给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, …)使得它们的和等于 n,返回和为 n 的完全平方数的 最少数量 。

示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4

示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9

同上题一样,求装满背包的物品个数,并且为组合问题

  • 背包大小:n
  • 物品价值 i * i

动态规划五部曲:

  • dp[j]含义: 和为j的完全平方数的最少数量为dp[j]

  • 确定递推公式: dp[j] 可以由dp[j - i * i]推出, dp[j - i * i] + 1 便可以凑成dp[j],所以递推公式:

    dp[j] = min(dp[j - i * i] + 1, dp[j]);
    
  • dp数组初始化: dp[0] = 0, 其他 INT_MAX

  • 确定遍历顺序: 由于是求物品的个数,这与排列和组合都可以,因此先遍历物品后背包和先背包后物品都可以的。

  • 举例推导dp数组:


程序实现:

// 任何一个正整数都可以 因为有 1
int numSquares(int n) 
{// dp[i] :  组成和为 i 的完全平方数的最少个数 最终求dp[n]vector<int> dp(n + 1, INT_MAX);// 物品重量 i  物品价值  i * i// 背包大小 n//递推公式//dp[j] = min(dp[j - i * i] + 1, dp[j])dp[0] = 0;// 遍历物品for(int  i = 1; i * i <= n; i++){// 遍历背包for(int j = i * i; j <= n; j++){	if(dp[j - i * i] != INT_MAX)dp[j] = min(dp[j - i * i]+1,dp[j]);}}for(int num:dp)cout << num << " ";return dp[n];
}

单词拆分

力扣链接

给定一个非空字符串 s 和一个包含非空单词的列表 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:
拆分时可以重复使用字典中的单词,并且字典中没有重复的单词。

示例 1:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true

示例 2:
输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false

思路:
物品:wordDict  wordDict[i]
背包:s

递推公式:

if((j,i) && dp[j]) // j-i是在字典单词 且dp[j] = truedp[i] = true;


动态规划五部曲:

  • dp[j]含义: 字符串长度为 i 能组成则为 true 结果dp[s.size()]

  • 确定递推公式:

    • 如果确定dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。
    • 所以递推公式是 if([j, i] 这个区间的子串出现在字典里 && dp[j]是true) 那么 dp[i] = true
  • dp数组初始化: dp[0] = true,递归的根基,非0下标dp[i]为false,只要没有被覆盖说明都是不可拆分为一个或多个在字典中出现的单词

  • 确定遍历顺序: 这里是完全背包排列问题,因为 “leet” “code” 与"code" "leet"是两种不同的情况,因此需要先遍历背包,后遍历物品。

  • 举例推导dp数组: 以输入: s = “leetcode”, wordDict = [“leet”, “code”]为例,dp状态如图:


程序实现:

bool wordBreak(string s, vector<string>& wordDict) 
{unordered_set<string> wordSet(wordDict.begin(), wordDict.end());// dp[i] :  字符串长度为 i 能组成则为 true  结果dp[s.size()]vector<bool> dp(s.size() + 1, false);// 物品   wordDict  wordDict[i]// 背包 	s	// 物品能否把背包装满 物品可以重复使用  // 递推公式/*if((j,i) && dp[j]) // j-i是在字典单词 且dp[j] = truedp[i] = true;*/// 排列 先遍历背包dp[0] = true;		// 递推公式的基础for(int i = 1; i <= s.size() ; i++){// 遍历物品for(int j = 0; j < i; j++){// 起始位置   距离string word = s.substr(j, i-j);// i-j的单词在字典中if(wordSet.find(word) != wordSet.end() && dp[j] == true)dp[i] = true;	}}for(int i = 0; i < dp.size(); i++)cout << dp[i] << " ";return dp[s.size()];
}

背包问题总结

背包问题总结

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

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

相关文章

零食店小程序发展客户转化运营

零食店、折扣店近些年市场中跑出了不少区域性、多地化的品牌&#xff0c;直营及加盟模式&#xff0c;还有各种超市、商场、街边小店等&#xff0c;零食基本没有年龄群体限制&#xff0c;又属于常消费品&#xff0c;线上线下生意都可以进行发展。 线下客户到店&#xff0c;线上…

链表数据结构

链表可以解决顺序表的缺点 我们今天简单引用下链表 这边是代码讲解 头文件 #pragma once #include<stdio.h> #include<iostream> #include<string.h> #include<stdlib.h> using namespace std; typedef struct student {union {int data;int len;};s…

【计网】从零开始掌握序列化与反序列化 --- 基础知识储备与程序重构

从零开始掌握序列化与反序列化 1 初识序列化与反序列化2 再谈Tcp协议3 程序重构3.1 Socket类3.2 回调函数设计3.3 最终的Tcp服务器类 1 初识序列化与反序列化 在刚学习计算机网络时&#xff0c;我们谈到过网络协议栈&#xff0c;其中最上层的就是应用层&#xff0c;那么这个应…

Rosetta 一:手把手教你用Linux安装Rosetta(全网最简洁)

文章目录 1. Rosetta 介绍2.下载2. Rosetta 安装3. 验证安装 1. Rosetta 介绍 很久很久之前就对Rosetta有所耳闻&#xff0c;有一篇文章叫做denovo protein design&#xff0c;说的就是用rosetta来设计蛋白质。 rosetta是david baker团队设计的软件&#xff0c;早期只是一个蛋…

【Godot4.3】胶囊形的偏移获取法

概述 之前用半圆弧拼接的方式求过胶囊形&#xff0c;在逐渐熟练使用Geometry2D的过程中&#xff0c;发现通过线段求端点是圆角类型的偏移多边形&#xff0c;获得的就是胶囊形。 所以我们有了第二种胶囊形求法。 测试代码 tool extends Node2D## 横向宽度 export var width:…

【工具】Windows|两款开源桌面窗口管理小工具Deskpins和WindowTop

总结 Deskpins 功能单一&#xff0c;拖到窗口上窗口就可以置顶并且标记钉子标签&#xff0c;大小 104 KB&#xff0c;开源位置&#xff1a;https://github.com/thewhitegrizzli/DeskPins/releases WindowTop 功能完善全面强大&#xff0c;包括透明度、置顶、选区置顶等一系列功…

SQL server学习01-SQL server环境配置

目录 一&#xff0c;手动下载及安装 microsoft .net framework 3.5 1&#xff0c;下载 2&#xff0c;安装 二&#xff0c;安装SQL server2014 1&#xff0c;下载 2&#xff0c;安装 3&#xff0c;启动SQL server服务 三&#xff0c;下载及安装Microsoft SQL Server…

C Prime Plus 第6章习题

你该逆袭了 红色标注的是&#xff1a;错误的答案 蓝色标注的是&#xff1a;正确的答案 绿色标注的是&#xff1a;做题时有疑问的地方 橙色标注的是&#xff1a;答案中需要着重注意的地方 练习题 一、复习题1、2、3、4、5、我的答案&#xff1a;错误正确答案&#xff1a; 6、7、…

ubuntu 安装minikube,并拉取k8s镜像

不要使用最新版&#xff0c;重要的事情说三遍&#xff0c;刚开始也是最求新一点的版本&#xff0c;但问题很多&#xff0c;主要是版本之间的依赖问题&#xff0c;不是某个依赖的版本不支持某些功能&#xff0c;就是依赖之间的版本不能对应上&#xff0c;所以就降低几个版本&…

行业人工智能研究-Python自监督方式学习图像表示算法

学术界人工智能研究落后于工业界 摘要 行业或工业界在人工智能研究上超出学术界&#xff0c;并占据着大量的计算力&#xff0c;数据集和人才诱人的薪水和明朗的预期吸引大量人才离开学术界&#xff0c;涌入行业或工业界即使&#xff0c;比如Meta开源其人工智能模型&#xff0…

实验:WLAN无线综合实验

无线综合实验的概述&#xff1a; WLAN无线综合实验是一种针对无线网络技术的综合性实验&#xff0c;旨在通过实践操作加深对无线局域网&#xff08;WLAN&#xff09;技术的理解和应用能力。以下是对该实验的详细概述&#xff1a; 实验目的 掌握认证AP上线的配置方法&#xff…

[SAP ABAP] 创建域

我们可以使用事务码SE11创建域 输入要创建的域的名称&#xff0c;然后点击创建 输入简短描述&#xff0c;选择数据类型和输入字符数 激活并保存域&#xff0c;创建的域才能够生效

pg入门18—如何使用pg gis

1. 下载postgre gis镜像 2. 运行镜像 docker run -p 15432:5432 -d -e POSTGRES_PASSWORDAb123456! postgis/postgis:12-3.4-alpine 3. 使用gis # 进入容器&#xff0c;登录pgdocker exec -it bash# 登录数据库psql -U postgres# 创建数据库CREATE DATABASE mygeotest;# 使用…

Spring Boot 入门:解锁 Spring 全家桶

前言 Spring 全家桶是现代 Java 开发者不可或缺的工具集&#xff0c;它提供了从轻量级的框架到微服务架构的完整支持。本文将带你快速了解 Spring 框架、核心概念如 IoC&#xff08;控制反转&#xff09;和 AOP&#xff08;面向切面编程&#xff09;&#xff0c;并深入介绍 Sp…

YOLOv10多模态 结合Transformer与NMS-Free 融合可见光+红外光(RGB+IR)双输入【附代码】

文章目录 前言视频效果代码获取文章概述必要环境一、模型训练1、 定义数据1.1、数据集结构1.2、定义data.yaml 2、 运行方法运行效果 二、模型验证运行方法运行效果 三、模型推理3.1. 推理图像1. 参数定义2. 运行方法运行效果 3.2. 推理视频1. 参数定义2. 运行方法运行效果 四、…

构建高可用和高防御力的云服务架构第一部分:深入解析DDoS高防(1/5)

引言 在数字化时代&#xff0c;网络安全已成为全球关注的焦点。随着互联网技术的快速发展和应用的广泛深入&#xff0c;网络安全形势日益严峻。特别是分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;以其破坏性强、难以防范的特点&#xff0c;对个人、企业乃至国…

Go-知识-定时器

Go-知识-定时器 1. 介绍2. Timer使用场景2.1 设定超时时间2.2 延迟执行某个方法 3. Timer 对外接口3.1 创建定时器3.2 停止定时器3.3 重置定时器3.4 After3.5 AfterFunc 4. Timer 的实现原理4.1 Timer数据结构4.1.1 Timer4.1.2 runtimeTimer 4.2 Timer 实现原理4.2.1 创建Timer…

Type-C 诱骗取电快充协议芯片,支持取电电压5V、9V、12V、15V、20V

‌XSP01A快充协议芯片‌是一款集成USB Power Delivery(PD) 2.0/3.0快充协议的USB-C/Type-C多功能取电芯片 它支持从手机充电器、车充等电源上取电给产品供电。这款芯片的优势在于其价格便宜&#xff0c;同时能够实现快充&#xff0c;对于不需要支持太多协议的设备来说&#x…

DRV8825步进电机驱动详细说明书————含接线图

最近玩步进电机时候&#xff0c;发现步进电机驱动种类多&#xff1b;A4988&#xff0c;drv8825,tb6600,lv8731……&#xff1b;tb6600驱动电流可达4A&#xff0c;1600细分&#xff0c;十分强大&#xff0c;但是体积大&#xff0c;用在平衡车上不太合适。 drv8825加散热器驱动电…

安装SQL Server遇到的问题

出现了一和二的问题&#xff0c;最后还是通过三完全卸载sqlserver安装成功了 一.安装过程中依次报错 1.MOF编译器无法连接WMI服务器。原因可能是语义错误(例如&#xff0c;与现有WMI知识库不兼容)或实际错误(例如WMI服务器启动失败)。 2.PerfLib 2.0计数器removal失败&#xf…