代码随想录训练营 Day32打卡 动态规划 part01 理论基础 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

news2024/11/13 16:15:11

代码随想录训练营 Day32打卡 动态规划 part01

一、 理论基础

动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。

例如:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

动态规划中dp[j]是由dp[j-weight[i]]推导出来的,然后取max(dp[j], dp[j - weight[i]] + value[i])。

但如果是贪心呢,每次拿物品选一个最大的或者最小的就完事了,和上一个状态没有关系。

对于动态规划问题,我将拆解为如下五步曲,这五步都搞清楚了,才能说把动态规划真的掌握了!

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

二、 力扣509. 斐波那契数

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。
示例
输入:n = 2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1

版本一

如果 n 为 0,直接返回 0,这是 Fibonacci 序列的第一个值。
创建一个长度为 n + 1 的数组 dp,用于存储从第 0 到第 n 位的 Fibonacci 值。
dp[0] 和 dp[1] 分别初始化为 0 和 1,对应 Fibonacci 序列的前两个值。
从 i = 2 开始遍历,使用状态转移方程 dp[i] = dp[i - 1] + dp[i - 2] 计算每个位置的 Fibonacci 值。
最终返回 dp[n],即为第 n 个 Fibonacci 数。

class Solution:
    def fib(self, n: int) -> int:
       
        # 排除 Corner Case,当 n 为 0 时,直接返回 0
        if n == 0:
            return 0
        
        # 创建 dp table 用于存储每个位置的 Fibonacci 值
        dp = [0] * (n + 1)

        # 初始化 dp 数组,Fibonacci 序列的前两个值
        dp[0] = 0
        dp[1] = 1

        # 遍历顺序: 由前向后。因为后面要用到前面的状态
        for i in range(2, n + 1):
            # 确定递归公式/状态转移公式
            dp[i] = dp[i - 1] + dp[i - 2]  # dp[i] 等于前两个状态的和
        
        # 返回答案,dp[n] 即为第 n 个 Fibonacci 数
        return dp[n]

版本二

如果 n 小于等于 1,直接返回 n,因为 Fibonacci(0) = 0 和 Fibonacci(1) = 1。
使用一个长度为 2 的列表 dp 来存储最近的两个 Fibonacci 值,初始为 [0, 1]。
从 i = 2 开始循环计算 Fibonacci 数,每次计算当前 Fibonacci 数并更新 dp 列表中的值。
返回 dp[1],即为第 n 个 Fibonacci 数。

class Solution:
    def fib(self, n: int) -> int:
        # 如果 n 小于等于 1,直接返回 n(因为 Fibonacci(0) = 0, Fibonacci(1) = 1)
        if n <= 1:
            return n
        
        # 初始化 dp 数组,只保存最近的两个 Fibonacci 数
        dp = [0, 1]
        
        # 从 2 开始计算到 n
        for i in range(2, n + 1):
            # 计算当前 Fibonacci 数,并更新 dp 数组
            total = dp[0] + dp[1]
            dp[0] = dp[1]  # 将 dp[1] 移到 dp[0] 位置
            dp[1] = total  # 新的 Fibonacci 数放在 dp[1] 位置
        
        # 返回 dp[1],即为第 n 个 Fibonacci 数
        return dp[1]

版本三

如果 n 小于等于 1,直接返回 n,因为 Fibonacci(0) = 0 和 Fibonacci(1) = 1。
使用 prev1 和 prev2 分别存储前两个 Fibonacci 数,初始为 0 和 1。
从 i = 2 开始计算,每次更新 prev1 和 prev2,其中 curr 是当前计算出的 Fibonacci 数。
返回 prev2,即为第 n 个 Fibonacci 数。

class Solution:
    def fib(self, n: int) -> int:
        # 如果 n 小于等于 1,直接返回 n
        if n <= 1:
            return n
        
        # 使用两个变量存储前两个 Fibonacci 数
        prev1, prev2 = 0, 1
        
        # 从 2 开始计算到 n
        for _ in range(2, n + 1):
            # 当前 Fibonacci 数是前两个数之和
            curr = prev1 + prev2
            # 更新 prev1 和 prev2
            prev1, prev2 = prev2, curr
        
        # 返回最后的 Fibonacci 数
        return prev2

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

三、 力扣70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶

爬到第一层楼梯有一种方法,爬到二层楼梯有两种方法。
那么第一层楼梯再跨两步就到第三层 ,第二层楼梯再跨一步就到第三层。
所以到第三层楼梯的状态可以由第二层楼梯 和 到第一层楼梯状态推导出来,那么就可以想到动态规划了。
dp[i]的定义:爬到第i层楼梯,有dp[i]种方法。

举例当n为5的时候,dp table(dp数组)应该是这样的
在这里插入图片描述

代码实现

如果 n 小于等于 1,直接返回 n。
通过使用两个变量 prev1 和 prev2 分别存储前两个台阶的方法数,进一步优化空间复杂度到 O(1)。
计算当前台阶的方法数 total,并更新 prev1 和 prev2,继续计算下一个台阶的方法数。
返回 prev2,即为到达第 n 级台阶的方法数。

# 空间复杂度为 O(1) 版本
class Solution:
    def climbStairs(self, n: int) -> int:
        # 处理边界情况,如果楼梯数为1或更少,直接返回n(0或1)
        if n <= 1:
            return n
        
        # 使用两个变量来存储前两个台阶的方法数,节省空间
        prev1 = 1  # 表示前两级台阶的方法数
        prev2 = 2  # 表示前一级台阶的方法数
        
        # 从第3级台阶开始计算
        for i in range(3, n + 1):
            # 当前台阶的方法数是前两个台阶的方法数之和
            total = prev1 + prev2
            prev1 = prev2  # 更新 prev1 为前一级台阶的方法数
            prev2 = total  # 更新 prev2 为当前台阶的方法数
        
        # 返回到达第n级台阶的方法数
        return prev2

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

四、力扣746. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。
你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。
请你计算并返回达到楼梯顶部的最低花费。
示例
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。

  • 支付 15 ,向上爬两个台阶,到达楼梯顶部。
    总花费为 15 。

dp[i]的定义:到达第i台阶所花费的最少体力为dp[i]。

对于dp数组的定义,大家一定要清晰!

可以有两个途径得到dp[i],一个是dp[i-1] 一个是dp[i-2]。

dp[i - 1] 跳到 dp[i] 需要花费 dp[i - 1] + cost[i - 1]。

dp[i - 2] 跳到 dp[i] 需要花费 dp[i - 2] + cost[i - 2]。

那么究竟是选从dp[i - 1]跳还是从dp[i - 2]跳呢?

一定是选最小的,所以dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2]);
cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] ,来模拟一下dp数组的状态变化,如下:

在这里插入图片描述

版本一

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        dp = [0] * (len(cost) + 1)
        dp[0] = 0  # 初始值,表示从起点开始不需要花费体力
        dp[1] = 0  # 初始值,表示经过第一步不需要花费体力
        
        for i in range(2, len(cost) + 1):
            # 在第i步,可以选择从前一步(i-1)花费体力到达当前步,或者从前两步(i-2)花费体力到达当前步
            # 选择其中花费体力较小的路径,加上当前步的花费,更新dp数组
            dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
        
        return dp[len(cost)]  # 返回到达楼顶的最小花费

版本二

class Solution:
    def minCostClimbingStairs(self, cost: List[int]) -> int:
        dp0 = 0  # 初始值,表示从起点开始不需要花费体力
        dp1 = 0  # 初始值,表示经过第一步不需要花费体力
        
        for i in range(2, len(cost) + 1):
            # 在第i步,可以选择从前一步(i-1)花费体力到达当前步,或者从前两步(i-2)花费体力到达当前步
            # 选择其中花费体力较小的路径,加上当前步的花费,得到当前步的最小花费
            dpi = min(dp1 + cost[i - 1], dp0 + cost[i - 2])
            
            dp0 = dp1  # 更新dp0为前一步的值,即上一次循环中的dp1
            dp1 = dpi  # 更新dp1为当前步的最小花费
        
        return dp1  # 返回到达楼顶的最小花费

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

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

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

相关文章

【leetcode】两数相加-25-4

方法&#xff1a;遍历 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), ne…

探讨MySQL中 “约束“ 下的查询

目录&#xff1a; 一. 数据库约束 二. 表的设计 三. 聚合查询 四.联合查询 一. 数据库约束&#xff1a; 1.约束类型汇总&#xff1a; 约束类型 说明 NULL约束使用NOT NULL指定列不为 空UNIQUE唯一约束指定列为唯一的、不重复的DEFAULT默认值约 …

Xchart 相关操作

using Newtonsoft.Json; using System.Collections; using System.Collections.Generic; using UnityEngine; using XCharts; /***************************************************************************** Copyright (C) 2013-2023 北京普源瑞新仿真科技有限公司 All Ri…

工作流(低代码)提升工作效率的秘密武器

如何看待“低代码”开发平台的兴起&#xff1f; 在当今快速变化的数字化时代&#xff0c;企业面临着前所未有的挑战和机遇。如何在激烈的市场竞争中脱颖而出&#xff0c;成为每个企业必须思考的问题。而低代码工具&#xff0c;正是帮助企业提升工作效率&#xff0c;实现快速响…

在Windows上用Visual Studio编译OpenCV

在Windows上编译开源项目&#xff0c;有时候让人痛不欲生&#xff0c;有时候却出奇地顺利。OpenCV属于后者。本文记录这次愉快的过程。 注&#xff1a;OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它提供了大…

移动学习平台小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;教师管理&#xff0c;学生管理&#xff0c;班级管理&#xff0c;课程分类管理&#xff0c;课程信息&#xff0c;作业信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0…

全面解析被低估的 Symbiosis — 一站式跨链 AMM DEX,跨链交易的未来

在区块链技术快速发展过程中&#xff0c;互操作性和流动性问题一直是行业面临的核心挑战。随着越来越多的区块链网络&#xff08;无论是 Layer 1 还是 Layer 2&#xff09;&#xff0c;以及不同虚拟机环境&#xff08;EVM 和非 EVM&#xff09;的出现&#xff0c;用户和开发者都…

P37-数据存储

数据类型介绍 前面学习了基本的内置类型&#xff1a; 以及它们所占存储空间的大小。 类型的意义&#xff1a; 1.使用这些类型开辟空间的大小&#xff08;大小决定了使用范围&#xff09; 2.如何看带内存空间的视角 类型的基本归类 整形家族 之所以char也分类在其中是因为实…

云原生时代的数据守护者:Velero 备份与迁移实战

项目背景 在云计算和容器技术飞速发展的今天&#xff0c;Kubernetes 已经成为容器编排和管理的事实标准。然而&#xff0c;随着业务的不断扩展&#xff0c;如何在云原生环境下保护和迁移 Kubernetes 集群资源&#xff0c;成为了摆在运维人员面前的一大挑战。Velero&#xff0c…

RazorSQL for Mac/Win:强大的跨平台多功能SQL数据库编辑器RazorSQL for Mac/Win:功能强大的跨平台 SQL 数据库编辑器

RazorSQL 是一款备受赞誉的多功能 SQL 数据库编辑器&#xff0c;适用于 Mac 和 Windows 操作系统&#xff0c;为用户提供了高效、便捷且强大的数据库管理和操作体验。 首先&#xff0c;RazorSQL 支持多种主流的数据库类型&#xff0c;包括但不限于 MySQL、Oracle、SQL Server、…

搭建内网开发环境(三)|基于nexus搭建docker私服

引言 上一篇教程中演示如果安装和 nexus 的基本使用&#xff0c;本篇教程将演示如果在 nexus 中搭建 docker 私服&#xff0c;并实战如何上传镜像到私服和从私服下载镜像。 搭建内网开发环境&#xff08;一&#xff09;&#xff5c;基于docker快速部署开发环境 搭建内网开发环…

MySQL基础--触发器,锁

触发器 触发器是与表有关的数据库对象&#xff0c;指在 insert/update/delete 之前或之后&#xff0c;触发并执行触发器中定义的 SQL 语句集合&#xff0c;触发器的这种特性可以协助应用在数据库端确保数据的完整性&#xff0c;日志记录&#xff0c;数据校验等操作。 使用别名 …

KNN 图像识别

KNN&#xff08;K-Nearest Neighbors&#xff0c;K最近邻&#xff09;算法是一种简单而有效的分类算法&#xff0c;也可以用于图像识别。它的基本思想是通过计算样本之间的距离&#xff0c;将待分类的样本归为其在训练集中最相近的K个样本所属的类别中最常见的类别 1. 准备工作…

阿里巴巴25校招内推

内推投递链接&#xff1a; http://aidc-jobs.alibaba.com/campus/qrcode/home?codeMx8ppk_s4MjkOFnb6XS3Vw%3D%3D 流程安排 简历投递(网申、内推) 8.16开启笔试&#xff1a;集中笔试为8月-10月面试&#xff1a;启动后持续推进offer发放&#xff1a;启动后持续推进 内推二维…

FPGA 综合笔记

仿真时阻塞赋值和非阻塞赋值 Use of Non-Blocking Assignment in Testbench : Verilog Use of Non-Blocking Assignment in Testbench : Verilog - Stack Overflow non-blocking assignment does not work as expected in Verilog non-blocking assignment does not work a…

华为APP审核,权限说明弹窗

工具类 import android.app.Activity; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.PopupWindow; import android.widget.TextV…

OD C卷 - 建立Huffman树

生成Huffman树 (100) 给定一个数值数组weights&#xff0c;每个值代表二叉树叶子节点的权值&#xff08;>1&#xff09;;根据权值数组&#xff0c;生成哈夫曼树&#xff0c;并中序遍历输出&#xff1b;左节点权值< 右节点权值&#xff0c;根节点权值为左右节点权值之和&…

git提交规范检查husky

一、Eslint 尤雨溪推荐的 prettierrc 配置&#xff0c;句尾不带分号 单引号。 尤雨溪推荐配置&#xff1a;vue-next/.prettierrc lint lint 是最著名的 C 语言工具之一&#xff0c;是由贝尔实验室 SteveJohnson 于 1979 在 PCC(PortableC Compiler) 基础上开发的静态代码分…

自己搭建远程桌面服务器-RustDesk 极简版

linux搭建RustDesk保姆间教程_rustdesk linux-CSDN博客https://blog.csdn.net/yzs2022/article/details/135136491 背景 在某公司工作&#xff0c;向日葵等远程办公软件均已屏蔽&#xff0c;无法使用&#xff08;也没有明文规定不允许使用远程控制软件&#xff09;&#xff0c…