代码随想录训练营 Day37打卡 动态规划 part05 完全背包理论基础 518. 零钱兑换II 377. 组合总和 Ⅳ 卡码70. 爬楼梯(进阶版)

news2024/9/22 23:33:22

代码随想录训练营 Day37打卡 动态规划 part05

一、完全背包理论基础

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

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

首先再回顾一下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]);

    }
}

二、力扣518. 零钱兑换II

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。
示例
输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

本题要求凑成总和的组合数,元素之间明确要求没有顺序。
那么本题,两个for循环的先后顺序可就有说法了。

我们先来看 外层for循环遍历物品(钱币),内层for遍历背包(金钱总额)的情况。

for (int i = 0; i < coins.size(); i++) { // 遍历物品
    for (int j = coins[i]; j <= amount; j++) { // 遍历背包容量
        dp[j] += dp[j - coins[i]];
    }
}

假设:coins[0] = 1,coins[1] = 5。

那么就是先把1加入计算,然后再把5加入计算,得到的方法数量只有{1, 5}这种情况。而不会出现{5, 1}的情况。

所以这种遍历顺序中dp[j]里计算的是组合数!

如果把两个for交换顺序,代码如下:

for (int j = 0; j <= amount; j++) { // 遍历背包容量
    for (int i = 0; i < coins.size(); i++) { // 遍历物品
        if (j - coins[i] >= 0) dp[j] += dp[j - coins[i]];
    }
}

背包容量的每一个值,都是经过 1 和 5 的计算,包含了{1, 5} 和 {5, 1}两种情况。

此时dp[j]里算出来的就是排列数!

输入: amount = 5, coins = [1, 2, 5] ,dp状态图如下:
在这里插入图片描述最后红色框dp[amount]为最终结果。

在求装满背包有几种方案的时候,认清遍历顺序是非常关键的。

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

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

代码实现

class Solution:
    def change(self, amount: int, coins: List[int]) -> int:
        # 初始化 dp 数组,其中 dp[i] 表示金额为 i 时的硬币组合数
        dp = [0] * (amount + 1)
        # 当金额为 0 时,只有一种方式(即不选择任何硬币)
        dp[0] = 1
        
        # 遍历每一种硬币面额
        for i in range(len(coins)):
            # 对于当前硬币面额 coins[i],遍历从 coins[i] 到 amount 的所有金额
            # 这样确保计算 dp[j] 时,dp[j - coins[i]] 已经计算完毕,符合动态规划的要求
            for j in range(coins[i], amount + 1):
                # 更新 dp[j],将当前面额的硬币考虑进来
                dp[j] += dp[j - coins[i]]
                # dp[j] 表示在考虑当前硬币后,凑成金额 j 的组合数
                # dp[j - coins[i]] 表示凑成金额 (j - 当前硬币面额) 的组合数
                # 将其加入 dp[j],表示加上当前硬币后,组合数的增加
                
        # 返回凑成总金额 amount 的硬币组合数
        return dp[amount]

力扣题目链接
题目文章讲解
题目视频讲解

三、力扣377. 组合总和 Ⅳ

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
示例
输入:nums = [1,2,3], target = 4
输出:7
解释
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。

本质是本题求的是排列总和,而且仅仅是求排列总和的个数,并不是把所有的排列都列出来。

dp[i]: 凑成目标正整数为i的排列个数为dp[i]

dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来。

因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的一部分。

在动态规划:494.目标和 (opens new window)和 动态规划:518.零钱兑换II (opens new window)中我们已经讲过了,求装满背包有几种方法,递推公式一般都是dp[i] += dp[i - nums[j]];

本题也一样。

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

所以本题遍历顺序最终遍历顺序:target(背包)放在外循环,将nums(物品)放在内循环,内循环从前到后遍历。

我们再来用示例中的例子推导一下:
在这里插入图片描述

代码实现

class Solution:
    def combinationSum(self, nums: List[int], target: int) -> int:
        # 初始化 dp 数组,dp[i] 表示凑成目标整数 i 的排列组合个数
        dp = [0] * (target + 1)
        # 当目标为 0 时,只有一种组合方式,即不选择任何元素
        dp[0] = 1
        
        # 遍历所有可能的目标数值 i,从 1 到 target
        for i in range(1, target + 1):  # 遍历背包容量(目标数值)
            # 遍历每个元素 nums[j]
            for j in range(len(nums)):  # 遍历物品(数组中的每个元素)
                # 如果当前目标 i 减去 nums[j] 之后,仍然是非负数
                # 说明 nums[j] 可以作为一个组成部分加入当前的排列组合中
                if i - nums[j] >= 0:
                    # 更新 dp[i],dp[i] 通过 dp[i - nums[j]] 转移而来
                    # dp[i - nums[j]] 是之前的目标值为 i - nums[j] 时的组合数
                    # 加入 nums[j] 后,这些组合可以形成目标 i
                    dp[i] += dp[i - nums[j]]
        
        # 返回凑成目标整数 target 的排列组合个数
        return dp[target]

力扣题目链接
题目文章讲解
题目视频讲解

四、卡码网70. 爬楼梯(进阶版)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例
输入描述:输入共一行,包含两个正整数,分别表示n, m
输出描述:输kama出一个整数,表示爬到楼顶的方法数。
输入:3 2
输出:3
提示信息
数据范围:
1 <= m < n <= 32;
当 m = 2,n = 3 时,n = 3 这表示一共有三个台阶,m = 2 代表你每次可以爬一个台阶或者两个台阶。
此时你有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶段
1 阶 + 2 阶
2 阶 + 1 阶

这其实是一个完全背包问题。1阶,2阶,… m阶就是物品,楼顶就是背包。

每一阶可以重复使用,例如跳了1阶,还可以继续跳1阶。

问跳到楼顶有几种方法其实就是问装满背包有几种方法。

此时大家应该发现这就是一个完全背包问题了!
dp[i]:爬到有i个台阶的楼顶,有dp[i]种方法。

本题呢,dp[i]有几种来源,dp[i - 1],dp[i - 2],dp[i - 3] 等等,即:dp[i - j]

那么递推公式为:dp[i] += dp[i - j]

这是背包里求排列问题,即:1、2 步 和 2、1 步都是上三个台阶,但是这两种方法不一样!

所以需将target放在外循环,将nums放在内循环。

代码实现

def climbing_stairs(n, m):
    # 初始化 dp 数组,dp[i] 表示爬到 i 个台阶有 dp[i] 种方法
    dp = [0] * (n + 1)  # 楼梯一共有 n 个台阶,索引从 0 到 n
    dp[0] = 1  # 爬到 0 阶的方法只有一种,即不爬

    # 遍历背包容量(即总台阶数)
    for j in range(1, n + 1):  # j 表示当前目标台阶数,从 1 到 n
        # 遍历每一种台阶步数(即可以选择的步数)
        for i in range(1, m + 1):  # i 表示每次可以爬的台阶数,从 1 到 m
            if j >= i:  # 如果当前目标台阶数 j 大于等于步数 i
                dp[j] += dp[j - i]  # 累加 dp[j - i],因为从 j - i 爬到 j 是一种可能的方案

    return dp[n]  # 返回爬到 n 阶的方法数

if __name__ == '__main__':
    # 从输入中获取 n 和 m
    n, m = list(map(int, input().split(' ')))
    # 打印结果,即爬到 n 阶的方法数
    print(climbing_stairs(n, m))

题目链接
题目文章讲解

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

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

相关文章

Postman【使用总结】--SpringBoot的Controller规范【重修】

【企业规范&#xff01;&#xff01;&#xff01;】 【响应数据】

提升学术论文质量的智能助手:ChatGPT

提升学术论文质量的智能助手&#xff1a;ChatGPT 前言ChatGPT的核心功能ChatGPT的优势具体应用案例局限性与最佳实践结语 前言 在这个知识爆炸的时代&#xff0c;学术研究已成为推动社会进步和科技发展的重要力量。每一篇论文的撰写&#xff0c;都是对人类知识边界的一次探索和…

攻防世界-web题型-2星难度汇总-个人wp

command_execution 典型的SSRF&#xff0c;先用命令找一下flag在哪里 xff_referer 修改一下xff和refere就可以了 php_rce 经典的thinkphp框架&#xff0c;闭着眼睛拿工具梭 这款工具无法直接getshell换一个 拿蚁剑直接连 Web_php_include 先分析代码 while (strstr($page,…

搜索二叉树进阶之AVL树

前言 二叉搜索树&#xff08;BST&#xff09;是一种基础的数据结构&#xff0c;能够高效地进行搜索、插入和删除操作。然而&#xff0c;在最坏的情况下&#xff0c;普通的BST可能会退化成一条链表&#xff0c;导致操作效率降低。为了避免这种情况&#xff0c;出现了自平衡二叉…

C语言-输出菱形

题目要求&#xff1a; 输出以下图形 程序&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() {int i, j;for (i 0; i < 4; i){for (j i 1; j < 4; j)printf(" ");for (j 0; j < 2 * i 1; j)printf("*");…

虽迟但到:Midjourney推出网页端并限时免费!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;专注于分享AI全维度知识&#xff0c;包括但不限于AI科普&#xff0c;AI工…

【最长公共子序列】

题目 代码 #include <bits/stdc.h> using namespace std;const int N 1010; int f[N][N]; char A[N], B[N]; int main() {int n, m;cin >> n >> m;cin >> A1 >> B1;for(int i 1; i < n; i){for(int j 1; j < m; j){if(A[i] B[j]) f[…

Linux三剑客-sedawk

一、三剑客-sed命令 1、格式 sed 找谁干啥 文件 找谁:条件&#xff0c;匹配哪一行&#xff0c;哪些行. 干啥:动作&#xff0c;增删改查. #显示文件的第3行 sed -n 3p /etc/passwd选项说明-n取消默认输出-p查找-rsed支持扩展正则-i修改文件内容&#xff0c;这个选项放在最后…

VS2017编译osg3.6.0和osgearth2.10

osg3.6.0正常编译即可&#xff0c;osgearth2.10编译过程中会出现如下错误 1.osgEarth出错 1>HTTPClient.obj : error LNK2019: 无法解析的外部符号 curl_global_init&#xff0c;该符号在函数 "public: static void __cdecl osgEarth::HTTPClient::globalInit(void)&…

【日常记录-Docker】基于Alibaba Cloud Linux3安装nodejs18

Author&#xff1a;赵志乾 Date&#xff1a;2024-08-23 Declaration&#xff1a;All Right Reserved&#xff01;&#xff01;&#xff01; 1. 问题 Alibaba Cloud Linux3基础镜像中携带的nodejs安装包版本为v14&#xff0c;与项目开发中使用的v18版本不同&#xff0c;需要更新…

数据库 —>数据库编程

数据库&#xff0c;用来保存信息&#xff0c;和文件有同样的作用&#xff0c;但是却有别于文件&#xff1b; 文件掉电不会消失&#xff0c;一般用来存储软件配置&#xff0c;想要保存的东西&#xff0c;他在查找的时候是一行一行的去查找&#xff0c;效率不高&#xff1b; 数据…

虚谷数据库连接断开-常见问题的排查及解决方法

在日常的数据库管理工作中。虚谷数据库连接断开是一个常见的问题&#xff0c;这不仅会影响数据库的性能&#xff0c;还可能导致应用程序无法正常运行&#xff0c;本文将探讨Xugu数据库连接断开的原因&#xff0c;并提供相应的解决方法。 E50022 与服务器间的连接已经断开,可能…

如何用ACME.SH实现SSL证书自动化管理?

在上篇《免费SSL证书有效期缩短至90天&#xff0c;该如何应对&#xff1f;》中&#xff0c;想必大家都已经get到了——建站必备四件套之SSL证书的有效期不断缩短已成不可逆的趋势。 这一趋势下&#xff0c;如何有效管理SSL证书成了一道难题。有机智的小伙伴反馈&#xff0c;使用…

golang(go语言)打包成带图标的 exe 可执行文件

目录 1、准备 ico 图标 2、生成 syso 文件 3、打包 4、效果 1、准备 ico 图标 2、生成 syso 文件 创建 main.rc 文件&#xff0c;rc文件的名称main 与项目根目录下 main.go的 main 同名 IDI_ICON1 ICON "favicon.ico" cmd 窗口运行命令 windres -o main.syso main…

从零开始编程:Go语言真的适合新手吗?

Go语言自诞生以来&#xff0c;一直以其简洁、高效和面向工程的特性受到开发者的青睐&#xff0c;尤其是在后端开发和并发编程方面&#xff0c;Go表现出了独特的优势。然而&#xff0c;作为一门以简单著称的语言&#xff0c;它是否适合作为编程初学者的第一门语言呢&#xff1f;…

电脑换硬盘怎么全盘克隆?轻松实现数据迁移

随着科技的不断发展&#xff0c;电脑硬盘的存储容量和读写速度也在不断提升。为了获得更好的电脑使用体验&#xff0c;许多用户会选择更换更大容量、更高效的硬盘。然而&#xff0c;在更换硬盘的过程中&#xff0c;一个关键的问题摆在了我们面前&#xff1a;如何将旧硬盘中的所…

一文掌握 Go 内存对齐

往期精选文章推荐&#xff1a; 深入理解 go map go 常用关键字 深入理解 Go 数组、切片、字符串 深入理解channel 深入理解 go context 深入 go interface 底层原理 深入理解 go reflect 深入理解 go unsafe 前言 在前面的文章 《深入理解 go reflect》和 《深入理解…

还在拼接字符串生成XML?(Java)

FreeMarker是一个功能强大的Java模板引擎&#xff0c;广泛应用于生成动态内容&#xff0c;如HTML、XML和其他文本格式。本文将介绍FreeMarker的基本使用方法&#xff0c;并提供一个更丰富的XML模板示例&#xff0c;以及模板标签和标识的含义。 1. 引入依赖 <dependency>…

Redis持久化RDB/AOF

一、RDB RDB&#xff08;Redis DataBase&#xff09; &#xff1a;RDB 持久性以指定的时间间隔执行数据集的时间点快照&#xff0c;就是把某一刻的数据和状态以文件的形式写到磁盘上。这个快照文件称为RDB文件&#xff08;dump.rdb&#xff09;。 自动触发 Redis7版本&#xff…

Jira使用指南(高级搜索JQL/统计/面板设计)

1.Jira使用指南 Jira使用指南.pdf 上面的pdf比较详细的介绍了Jira的使用&#xff0c;目录如下&#xff1a; 或者从Jira的中文官网获得更多的使用指南 JIRA入门教程 1.1JIRA如何根据过滤出我关注的单子 https://www.cnblogs.com/wzxbro/p/17203914.html 登录JIRA平台&#…