力扣练习 3.27

121. 买卖股票的最佳时机

贪婪思想:力争在最低成本买入,最高利润卖出。
[7,1,5,3,6,4]
可以先假设在第一天买入和卖出,这时最低成本是7,最大利润是7-7=0
然后假设在第二天买入和卖出,成本就是1,利润也是0
第三天因为成本比第二天高,所以不买入,只卖出,利润就是5-1=4
以此类推
每次遍历都检测最小成本和最大利润

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 贪婪思想,初始化成本和利润
        cost, profit = float('inf'), 0
        # 成本是取最小的,利润是选最大的
        # 遍历过程中不断更新最小成本和最大利润
        for price in prices:
            cost = min(cost, price)
            profit = max(profit, price-cost)
        return profit


122. 买卖股票的最佳时机 II

单独的交易:看今天卖和昨天买的差值
连续上涨:等差数列

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        # 初始化利润
        profit = 0
        # 如果当前比前一天的价格更高,那就是要昨天买今天买
        # 对于等差数列,在同一天卖跟每天都卖是一样的
        for i in range(1, len(prices)):
            # 把所有天的利润加起来,如果是负利润,那么就是0(不卖)
            temp = max(prices[i]-prices[i-1], 0)
            profit += temp

        return profit

300. 最长递增子序列

要找到一个数组中最长严格递增子序列(Longest Increasing Subsequence,简称LIS)的长度,动态规划是一种有效的方法。这个问题的关键在于找出一个状态定义,使得状态之间的转移是可解的。

动态规划解法:

  1. 状态定义:定义dp[i]为考虑前i个元素,以第i个数字结尾的最长递增子序列的长度。注意,这里的子序列至少包含一个数,即第i个数本身。

  2. 状态转移方程:要计算dp[i],需要考虑比第i个数小的所有数。对于任意一个小于i的下标j,如果nums[j] < nums[i],那么nums[i]可以接在nums[j]后面形成一个更长的递增子序列。因此,dp[i] = max(dp[i], dp[j] + 1)对所有的0 <= j < inums[j] < nums[i]

  3. 初始化:因为最长递增子序列至少包含自己,所以每个元素的LIS长度初始值至少为1,即dp[i] = 1

  4. 计算顺序:从左到右依次计算每个dp[i],因为计算dp[i]时需要用到前面的dp[j]

  5. 结果:最长递增子序列的长度为所有dp[i]中的最大值。

代码实现

def lengthOfLIS(nums):
    if not nums:
        return 0

    n = len(nums)
    dp = [1] * n  # 初始化dp数组,每个元素的LIS长度初始值为1

    for i in range(1, n):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)

    return max(dp)  # LIS长度为dp数组中的最大值

1143. 最长公共子序列

解题思路:动态规划
需要构建辅助矩阵,多加一行和一列
当前的状态由左上、左边和上边决定。如果相等,那么当前状态是由左对角线(左上角)的加自己1决定的;如果不相等,那么当前状态需要在左边和上边中选择一个最大的扩展其状态。

步骤 1:

理解问题和目标 我们的目标是找出两个字符串text1text2的最长公共子序列的长度。一个子序列是指从原字符串中删除一些(或不删除)字符而不改变其余字符的顺序得到的新字符串。

步骤 2:

定义 DP 数组 定义二维数组dp,其中dp[i][j]表示text1中前i个字符和text2中前j个字符的最长公共子序列的长度。注意,dp[i][j]是基于text1[0..i-1]text2[0..j-1]的子序列。

步骤 3:

找出状态转移方程

  1. 如果text1[i-1] == text2[j-1],即当前两个字符相等,那么dp[i][j] = dp[i-1][j-1] + 1。因为我们找到了一个公共元素,可以在以前的LCS长度上加1。
  2. 如果text1[i-1] != text2[j-1],即当前两个字符不相等,那么dp[i][j] = max(dp[i-1][j], dp[i][j-1])。此时,LCS的长度是两种情况中的较大值。

步骤 4:

初始化 DP 数组 初始化dp数组的第一行和第一列为0,因为任何字符串与空字符串的最长公共子序列长度都是0。

步骤 5:

填充 DP 数组 从dp[1][1]开始,根据状态转移方程逐步填充整个dp数组。

步骤 6:

解读结果 dp数组的最后一个元素dp[len(text1)][len(text2)]即为text1text2的最长公共子序列的长度。

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        # 辅助矩阵
        m, n = len(text1), len(text2)
        # 都加1个维度,方便后续操作不超索引
        dp = [[0]*(m+1) for _ in range(n+1)]

        for i in range(1, n+1): # 先从第一个维度遍历
            for j in range(1, m+1): #从第二个维度遍历
                if text1[j-1] == text2[i-1]: # 因为是从1开始,所以要看前面的一位
                    dp[i][j] = dp[i-1][j-1]+1 # 相等,那么就是加上前面的最长子序列和当前的元素
                else:
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]) # 不相等,就将左边对角线附近的最长子序列数扩展到当前
                
        return dp[n][m]

64. 最小路径和

动态规划
建立一个辅助矩阵,第一行和第一列的路径和只能是从左边或者右边来,所以辅助矩阵维度保持不变,初始化为0,将第一行第一列的值按照路径和定义加进来。
状态转移公式就是dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

class Solution:
    def minPathSum(self, grid: List[List[int]]) -> int:

        m = len(grid)
        n = len(grid[0])

        dp = [[0] * (n) for _ in range(m)]

        # 初始化起点
        dp[0][0] = grid[0][0]

        # 初始化第一列,每个位置只能从上方来
        for i in range(1, m):
            dp[i][0] = dp[i-1][0] + grid[i][0]

        # 初始化第一行,每个位置只能从左边来
        for j in range(1, n):
            dp[0][j] = dp[0][j-1] + grid[0][j]

        # 动态规划
        for i in range(1, m):
            for j in range(1, n):
                # 选择左边和上面的最小值加上当前值
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

        return(dp[-1][-1])  # 输出最后一个位置的值,即右下角的最小路径和

72. 编辑距离

解题思路:动态规划,不用管具体改了什么字符,只管当前状态由什么决定。一般都是跟左边、上边、左上有关。

解释这三个操作对应的状态转移方程,我们先要理解每个操作的含义和如何影响字符串的转换:

  1. 插入dp[i][j] = dp[i][j-1] + 1

    • 这意味着为了从word1的前i个字符转换到word2的前j个字符,我们考虑了在word1的前i个字符基础上,通过在末尾插入一个与word2[j-1]相同的字符,使其与word2的前j个字符匹配。因为我们插入了一个字符,所以操作次数加1。
    • 插入操作是基于假设我们已经找到了将word1的前i个字符转换为word2的前j-1个字符的最优方法,并在此基础上进行一次插入操作。
  2. 删除dp[i][j] = dp[i-1][j] + 1

    • 这表示为了将word1的前i个字符转换为word2的前j个字符,我们考虑了从word1的前i个字符中删除最后一个字符,使其与word2的前j个字符更接近。因为我们进行了删除操作,所以操作次数加1。
    • 删除操作是基于假设我们已经找到了将word1的前i-1个字符转换为word2的前j个字符的最优方法,并在此基础上进行一次删除操作。
  3. 替换dp[i][j] = dp[i-1][j-1] + 1

    • 这意味着为了从word1的前i个字符转换到word2的前j个字符,我们考虑了将word1的第i个字符替换为word2的第j个字符(这两个字符不同),使之匹配。因为我们替换了一个字符,所以操作次数加1。
    • 替换操作是基于假设我们已经找到了将word1的前i-1个字符转换为word2的前j-1个字符的最优方法,并在此基础上进行一次替换操作。

这些状态转移方程的核心思想是,通过局部最优解(即最少的操作次数)来逐步构建全局最优解。在每个位置dp[i][j],我们都尝试三种操作,并从中选择一种使得总操作数最小的方案,以此保证最终得到的是将整个word1转换为word2所需的最少操作次数。

class Solution:
    def minDistance(self, word1: str, word2: str) -> int:
        m,n = len(word1), len(word2)

        # 动态规划
        dp = [[0]*(n+1) for _ in range(m+1)]

        # 初始化左上角
        dp[0][0] = 0
        # 初始化第一行
        for i in range(1, m+1):
            dp[i][0] = i
        # 初始化第一列
        for j in range(1, n+1):
            dp[0][j] = j

        for i in range(1, m+1):
            for j in range(1, n+1):
                # 如果字符相等
                if word1[i-1] == word2[j-1]:
                    dp[i][j] = dp[i-1][j-1] # 那就不操作,继承以前的操作
                else:
                    dp[i][j] = min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) + 1 # 否则,就操作,加上当前字符

        
        return(dp[m][n])

62. 不同路径

解题思路:又是动态规划。
要确定的点是当前状态是个啥,比如上题就是操作数,这题就是路径数;
然后确定当前状态被什么决定,比如上题是根据左上、左边、上边,本题就是根据左边和上边,理解题意后写出状态转移公式,代码化。

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # 辅助矩阵
        dp = [[1]*n for _ in range(m)]
        # 状态转移公式
        for i in range(1, m):
            for j in range(1, n):
                # 当前状态是指从原点到当前点的路径总和,由从上和左边来的路径决定
                dp[i][j] = dp[i-1][j] + dp[i][j-1]

        return(dp[-1][-1])

考虑到机器人每次只能向下或者向右移动,我们可以发现到达网格中任意位置 (i, j) 的路径数是到达其上方位置 (i-1, j)
和左侧位置 (i, j-1) 的路径数之和。这是因为,从起点到达 (i, j) 的每条路径,都是通过最后一步从 (i-1, j)
(i, j-1) 移动过来的。基于这个原理,我们可以构建一个动态规划方程来求解问题。

动态规划方法

  1. 状态定义:定义 dp[i][j] 为从起点 (0, 0) 到达点 (i, j) 的路径总数。

  2. 状态转移方程:因为到达点 (i, j) 只能从 (i-1, j)(i, j-1) 两个点移动一步到达,所以 dp[i][j] = dp[i-1][j] + dp[i][j-1]

  3. 初始化:初始化第一行和第一列的值为1,因为从起点到达第一行和第一列的任意位置只有一种路径(要么一直向右,要么一直向下)。

  4. 填表:按顺序计算 dp 数组的其余部分。

  5. 结果dp[m-1][n-1] 即为从起点到达终点的路径总数。

解释

  • 初始化时,dp 的每个元素都被设置为1,因为到达第一行或第一列的任意位置的路径只有一条。
  • 通过双层循环,我们更新 dp 数组的值,使其反映到达每个位置的路径数。
  • 最后,dp[m-1][n-1] 存储了到达右下角的路径总数。

这种动态规划方法利用了问题的子结构(问题的解可以通过其子问题的解来构建)和重叠子问题(相同的子问题被多次计算)特点,使得我们可以高效地求解原问题。

152. 乘积最大子数组

要解决这个问题,我们可以同时跟踪到当前位置为止的最大乘积和最小乘积(因为一个很小的负数乘以一个负数可能变成一个很大的正数)。这意味着我们需要维护两个动态规划数组,一个用于追踪最大乘积,另一个用于追踪最小乘积。

修改后的算法:

  1. 定义状态

    • max_dp[i] 表示到第i个元素为止的最大连续子数组乘积。
    • min_dp[i] 表示到第i个元素为止的最小连续子数组乘积(考虑负负得正的情况)。
  2. 状态转移方程

    • 对于每个i,有三种情况需要考虑(nums[i]本身、nums[i] * max_dp[i-1]nums[i] * min_dp[i-1]),因为当前元素可以独自成为最大乘积子数组,或者可以与前一个子数组的最大/最小乘积相乘形成新的最大/最小乘积。
    • max_dp[i] = max(nums[i], nums[i] * max_dp[i-1], nums[i] * min_dp[i-1])
    • min_dp[i] = min(nums[i], nums[i] * max_dp[i-1], nums[i] * min_dp[i-1])
  3. 初始化

    • max_dp[0] = min_dp[0] = nums[0],因为第一个元素前面没有其他元素,所以它自己就是一个子数组。
  4. 遍历数组

    • 按照状态转移方程更新max_dpmin_dp
  5. 找到最大乘积

    • 最大乘积不一定是max_dp[-1],因为最大乘积可能出现在数组的任何位置,所以需要遍历max_dp数组找到最大值。
class Solution:
    def maxProduct(self, nums: List[int]) -> int:
        # 因为负负得正,所以不能只看大于0的,维护两个辅助数组
        max_dp = [1] * len(nums)
        min_dp = [1] * len(nums)
        max_dp[0] = nums[0]
        min_dp[0] = nums[0]
        # 最大乘积的数组状态由当前元素;如果前面是正的,乘当前;最小乘积有可能是负的,当前也可能是负的
        for i in range(1, len(nums)):
            max_dp[i] = max(nums[i], max_dp[i-1]*nums[i], min_dp[i-1]*nums[i])
            min_dp[i] = min(nums[i], max_dp[i-1]*nums[i], min_dp[i-1]*nums[i])
        
        return max(max_dp)

198. 打家劫舍

问题的关键是找到一个状态定义,以及如何根据前面的状态来更新当前的状态。问题可以描述为:给定一个整数数组,从中选取出一些元素,这些元素的总和最大,但不能选取相邻的元素。

动态规划思路

  1. 状态定义:定义dp[i]为到达第i个房屋时能够偷窃到的最高金额。

  2. 状态转移方程:对于第i个房屋,有两种选择:

    • 不偷这个房屋,那么总金额就是到前一个房屋为止的最高金额,即dp[i-1]
    • 偷这个房屋,那么总金额就是这个房屋的金额加上到前前个房屋为止的最高金额,即nums[i] + dp[i-2](因为不能偷相邻的房屋)。
      所以,dp[i] = max(dp[i-1], nums[i] + dp[i-2])
  3. 初始化

    • dp[0] = nums[0],只有一个房屋时,只能偷这一个;
    • 如果有两个房屋,应该选择金额较大的那个,所以dp[1] = max(nums[0], nums[1])
  4. 计算顺序:从左到右依次计算每个dp[i]

class Solution:
    def rob(self, nums: List[int]) -> int:
        # 处理只有一个元素的情况
        if len(nums) == 1:
            return nums[0]
        # 直接复制数组作为辅助数组
        dp = nums.copy()
        # 第二个状态取前两个的最大值
        dp[1] = max(nums[0], nums[1])
        # dp的当前状态是到当前的最高金额(和)
        for i in range(2, len(nums)):
            # 要么不偷,要么偷
            dp[i] = max(dp[i-1], dp[i-2] + nums[i])

        return dp[-1]

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTTP

HTTP 概念&#xff1a;HyperTextTransferProtocol&#xff0c;超文本传输协议&#xff0c;规定了浏览器和服务器之间数据传输的规则 HTTP协议特点&#xff1a; 1.基于TCP协议&#xff1a;面向连接&#xff0c;安全 2.基于请求-响应模型的&#xff1a;一次请求对应一次响应 …

pipeline script for SCM 构建go项目

pipeline script 和 pipeline script for SCM 推荐使用第二种 首先需要再项目的根目录下新建Jenkinsfile 文件 pipeline for SCM 拉取github 代码 自动生成Jenkinsfile 的语法 生成jenkinsfile 的拉取脚本 项目中编写Jenkinsfile文件 pipeline {agent anystages …

如何评估户外LED显示屏的质量标准

随着数字媒体的不断进步&#xff0c;户外LED显示屏已经成为现代城市不可或缺的一部分&#xff0c;它们以鲜明的视觉冲击力和广泛的应用范围&#xff0c;成为了广告和公共信息传播的重要工具。然而&#xff0c;并非所有的户外LED显示屏都能满足高标准的户外使用要求。为了确保投…

MoonBit MeetUp回顾——张正、宗喆:编程语言在云原生与区块链领域的技术探索

宗喆和张正分别给我们带了 KCL 相关的最新进展&#xff0c;由蚂蚁集团开发的 Rust 编写的开源 DSL&#xff0c;目标是优化云原生策略配置和用户体验。它通过引入动态配置管理、配置校验和基础设施抽象等核心概念&#xff0c;解决开发者认知负担、配置膨胀和标准化工具缺乏的问题…

国内外主要气象卫星介绍

NOAA AVHRR介绍 美国NOAA极轨卫星从1970年12月第一颗发射以来&#xff0c;近40年连续发射了18颗&#xff0c;最新的NOAA-19也将在2009年发射升空。NOAA卫星共经历了5代&#xff0c;目前使用较多的为第五代NOAA卫星&#xff0c;包括NOAA-15—NOAA-18&#xff1b;作为备用的第四…

【STM32CubeMX(1)】开发环境搭建

1、安装STM32CubeMX 安装前言&#xff1a;软件是免费的&#xff0c;网上安装教程也是很丰富&#xff0c;我就不造轮子了。 1.1 准备java-jdk环境 参考&#xff1a;Java环境配置|菜鸟教程 1.2 下载STM32CubeMX 获取安装包&#xff1a;STM32CubeMX - STM32Cube initializati…

verilog 从入门到看得懂---verilog 的基本语法各种语句

本篇文章主要介绍verilog里面常用的语句&#xff0c; 包括条件语句、循环语句块语句和生成语句。出了块语句和生成语句&#xff0c;其他的基本和c语言或者m语言一致。 1&#xff0c;if 语句&#xff0c;在需要判断逻辑的时候可以使用if语句&#xff0c;如 从输入a&#xff0c;…

SpringCloud实用篇(一)

1.SpringCloud SpringCloud是目前国内使用最广泛的微服务框架。官网地址&#xff1a;Spring Cloud SpringCloud集成了各种微服务功能组件&#xff0c;并基于SpringBoot实现了这些组件的自动装配&#xff0c;从而提供了良好的开箱即用体验&#xff1a; SpringCloud与SpringBoo…

Python中的排序算法:归并排序,选择排序和快速排序详解

排序算法是计算机科学中的一个基础且重要的概念。它用于将一组数据&#xff08;如数字、字符串等&#xff09;按照某种顺序&#xff08;升序或降序&#xff09;重新排列。在Python中&#xff0c;我们有许多内置的函数和库可以方便地实现排序&#xff0c;但理解排序算法的基本思…

Netty对Channel事件的处理以及空轮询Bug的解决

继续上一篇Netty文章&#xff0c;这篇文章主要分析Netty对Channel事件的处理以及空轮询Bug的解决 当Netty中采用循环处理事件和提交的任务时 由于此时我在客户端建立连接&#xff0c;此时服务端没有提交任何任务 此时select方法让Selector进入无休止的阻塞等待 此时selectCnt进…

企业员工培训考试系统开发方案

一、引言 在当今知识经济时代&#xff0c;企业对员工的综合素质和专业技能有着越来越高的要求。为了适应这一趋势&#xff0c;构建一个全面而高效的企业员工培训考试系统变得尤为重要。该系统旨在通过提供多样化的培训课程和全面的考核机制&#xff0c;促进员工持续学习和能力…

24/03/28总结

抽象类&#xff1a; 将共性的方法抽取到父类之后。由于每一个子类执行的内容是不一样&#xff0c;所以&#xff0c;在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。 而为什么不直接在子类中定义方法&#xff1a;项目的完成不是一个人&#xff0c;如果有时忘记写方…

【教学类-40-09】A4骰子纸模制作9.0(3.47CM嵌套骰子 一条8格便于对折,表格相连 一页3个 油墨打印A4铅画纸)

作品展示 背景需求&#xff1a; 骰子调整到第8版&#xff0c;把骰子图案作成一长条&#xff0c;便于切割裁剪。 【教学类-40-08】A4骰子纸模制作8.0&#xff08;2.97CM嵌套骰子表格相连 一页7个 油墨打印A4铅画纸&#xff09;-CSDN博客文章浏览阅读929次&#xff0c;点赞20次…

幻兽帕鲁服务器价格表_阿里云/腾讯云/京东云/华为云报价大全

2024年全网最全的幻兽帕鲁服务器租用价格表&#xff0c;阿里云幻兽帕鲁游戏服务器26元1个月、腾讯云32元一个月、京东云26元一个月、华为云24元1个月&#xff0c;阿腾云atengyun.com整理最新幻兽帕鲁专用4核16G、8核16G、8核32G游戏服务器租用价格表大全&#xff1a; 阿里云幻…

土壤有机质空间分布数据

土壤有机质&#xff08;soil organic matter&#xff09;是土壤中含碳有机化合物的总称&#xff0c;包括土壤固有的和外部加入的所有动植物残体及其分解产物和合成产物。主要来源于动植物及微生物残体&#xff0c;可分为腐殖质和非腐殖物质。一般占土壤固相总重的10%以下&#…

推荐:便宜幻兽帕鲁Palworld联机游戏服务器优惠价格表

2024年全网最全的幻兽帕鲁服务器租用价格表&#xff0c;阿里云幻兽帕鲁游戏服务器26元1个月、腾讯云32元一个月、京东云26元一个月、华为云24元1个月&#xff0c;阿腾云atengyun.com整理最新幻兽帕鲁专用4核16G、8核16G、8核32G游戏服务器租用价格表大全&#xff1a; 阿里云幻…

思维升级之路:观察思维深层,解锁认知新境界

目录 一、观察我们的思维习惯 二、人类有哪些思维方法 三、为什么要多使用归纳推理而不是演绎推理 四、转变思维的关键 - 觉察 怎么提升自身的认知水平&#xff1f;这篇文章里&#xff0c;作者尝试对思维模式这件事做出探讨&#xff0c;一起来看看&#xff0c;或许可以帮你…

2023年蓝桥杯省赛——矩形面积总和

目录 题目链接&#xff1a; 1.矩形总面积 - 蓝桥云课 (lanqiao.cn) 思路 暴力 数学杯思路 数学逻辑 难点之重合区域 代码实现 总结 题目链接&#xff1a; 1.矩形总面积 - 蓝桥云课 (lanqiao.cn) 思路 暴力 开幕雷击&#xff0c;我直接开始暴力&#xff0c;但是我暴力…

Java学习之方法

目录 方法 方法声明格式&#xff1a; 调用方式&#xff1a; 详细说明 示例 --方法的声明及调用 语句块 练习 方法的重载(overload) 构成条件 示例 --方法重载 递归结构 缺陷 方法 方法(method)&#xff1a;一段用于完成特定功能的代码片段&#xff0c;类似于其他语…