首页 > 编程语言 >代码随想录算法训练营第二十八天 | 122.买卖股票的最佳时机II , 55. 跳跃游戏 , 45.跳跃游戏II , 1005.K次取反后最大化的数组和

代码随想录算法训练营第二十八天 | 122.买卖股票的最佳时机II , 55. 跳跃游戏 , 45.跳跃游戏II , 1005.K次取反后最大化的数组和

时间:2024-08-13 23:23:17浏览次数:14  
标签:下标 游戏 覆盖 nums II prices 跳跃 步数 贪心

目录

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

思路

方法一: 贪心

方法二:动态规划

55. 跳跃游戏

思路

方法一:使用while循环

方法二:使用for循环

45.跳跃游戏II 

思路

方法一

方法二

方法一:贪心方法一

方法二:贪心方法二

 方法三:贪心方法三

心得体会

1005.K次取反后最大化的数组和

思路

方法一:贪心

心得体会


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

思路

这道题目可能我们只会想,选一个低的买入,再选个高的卖,再选一个低的买入.....循环反复。

如果想到其实最终利润是可以分解的,那么本题就很容易了!

如何分解呢?

假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。

相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。

此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑!

那么根据 prices 可以得到每天的利润序列:(prices[i] - prices[i - 1]).....(prices[1] - prices[0])。

如图:

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

一些同学陷入:第一天怎么就没有利润呢,第一天到底算不算的困惑中。

第一天当然没有利润,至少要第二天才会有利润,所以利润的序列比股票序列少一天!

从图中可以发现,其实我们需要收集每天的正利润就可以,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间

那么只收集正利润就是贪心所贪的地方!

局部最优:收集每天的正利润,全局最优:求得最大利润

局部最优可以推出全局最优,找不出反例,试一试贪心!

方法一: 贪心

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        result = 0
        for i in range(1, len(prices)):
            result += max(prices[i] - prices[i - 1], 0)
        return result

方法二:动态规划

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        length = len(prices)
        dp = [[0] * 2 for _ in range(length)]
        dp[0][0] = -prices[0]
        dp[0][1] = 0
        for i in range(1, length):
            dp[i][0] = max(dp[i-1][0], dp[i-1][1] - prices[i]) #注意这里是和121. 买卖股票的最佳时机唯一不同的地方
            dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
        return dp[-1][1]

55. 跳跃游戏

思路

刚看到本题一开始可能想:当前位置元素如果是 3,我究竟是跳一步呢,还是两步呢,还是三步呢,究竟跳几步才是最优呢?

其实跳几步无所谓,关键在于可跳的覆盖范围!

不一定非要明确一次究竟跳几步,每次取最大的跳跃步数,这个就是可以跳跃的覆盖范围。

这个范围内,别管是怎么跳的,反正一定可以跳过来。

那么这个问题就转化为跳跃覆盖范围究竟可不可以覆盖到终点!

每次移动取最大跳跃步数(得到最大的覆盖范围),每移动一个单位,就更新最大覆盖范围。

贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围),整体最优解:最后得到整体最大覆盖范围,看是否能到终点

局部最优推出全局最优,找不出反例,试试贪心!

如图:

i 每次移动只能在 cover 的范围内移动,每移动一个元素,cover 得到该元素数值(新的覆盖范围)的补充,让 i 继续移动下去。

而 cover 每次只取 max(该元素数值补充后的范围, cover 本身范围)。

如果 cover 大于等于了终点下标,直接 return true 就可以了。

方法一:使用while循环

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        cover = 0
        if len(nums) == 1: return True
        i = 0
        # python不支持动态修改for循环中变量,使用while循环代替
        while i <= cover:
            cover = max(i + nums[i], cover)
            if cover >= len(nums) - 1: return True
            i += 1
        return False

方法二:使用for循环

## for循环
class Solution:
    def canJump(self, nums: List[int]) -> bool:
        cover = 0
        if len(nums) == 1: return True
        for i in range(len(nums)):
            if i <= cover:
                cover = max(i + nums[i], cover)
                if cover >= len(nums) - 1: return True
        return False

45.跳跃游戏II 

思路

本题相对于55.跳跃游戏 思路是相似的,还是要看最大覆盖范围。

本题要计算最少步数,那么就要想清楚什么时候步数才一定要加一呢?

贪心的思路,局部最优:当前可移动距离尽可能多走,如果还没到终点,步数再加一。整体最优:一步尽可能多走,从而达到最少步数。

思路虽然是这样,但在写代码的时候还不能真的能跳多远就跳多远,那样就不知道下一步最远能跳到哪里了。

所以真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!

这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖

如果移动下标达到了当前这一步的最大覆盖最远距离了,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。

如图:

45.跳跃游戏II

图中覆盖范围的意义在于,只要红色的区域,最多两步一定可以到!(不用管具体怎么跳,反正一定可以跳到)

方法一

从图中可以看出来,就是移动下标达到了当前覆盖的最远距离下标时,步数就要加一,来增加覆盖距离。最后的步数就是最少步数。

这里还是有个特殊情况需要考虑,当移动下标达到了当前覆盖的最远距离下标时

  • 如果当前覆盖最远距离下标不是是集合终点,步数就加一,还需要继续走。
  • 如果当前覆盖最远距离下标就是是集合终点,步数不用加一,因为不能再往后走了。

方法二

依然是贪心,思路和方法一差不多,代码可以简洁一些。

针对于方法一的特殊情况,可以统一处理,即:移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不考虑是不是终点的情况。

想要达到这样的效果,只要让移动下标,最大只能移动到 nums.size - 2 的地方就可以了。

因为当移动下标指向 nums.size - 2 时:

  • 如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即 ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图: 

    45.跳跃游戏II2

  • 如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。如图:

45.跳跃游戏II1

 

方法一:贪心方法一

class Solution:
    def jump(self, nums):
        if len(nums) == 1:
            return 0
        
        cur_distance = 0  # 当前覆盖最远距离下标
        ans = 0  # 记录走的最大步数
        next_distance = 0  # 下一步覆盖最远距离下标
        
        for i in range(len(nums)):
            next_distance = max(nums[i] + i, next_distance)  # 更新下一步覆盖最远距离下标
            if i == cur_distance:  # 遇到当前覆盖最远距离下标
                ans += 1  # 需要走下一步
                cur_distance = next_distance  # 更新当前覆盖最远距离下标(相当于加油了)
                if next_distance >= len(nums) - 1:  # 当前覆盖最远距离达到数组末尾,不用再做ans++操作,直接结束
                    break
        
        return ans

方法二:贪心方法二

class Solution:
    def jump(self, nums):
        cur_distance = 0  # 当前覆盖的最远距离下标
        ans = 0  # 记录走的最大步数
        next_distance = 0  # 下一步覆盖的最远距离下标
        
        for i in range(len(nums) - 1):  # 注意这里是小于len(nums) - 1,这是关键所在
            next_distance = max(nums[i] + i, next_distance)  # 更新下一步覆盖的最远距离下标
            if i == cur_distance:  # 遇到当前覆盖的最远距离下标
                cur_distance = next_distance  # 更新当前覆盖的最远距离下标
                ans += 1
        
        return ans

 方法三:贪心方法三

class Solution:
    def jump(self, nums) -> int:
        if len(nums)==1:  # 如果数组只有一个元素,不需要跳跃,步数为0
            return 0
        
        i = 0  # 当前位置
        count = 0  # 步数计数器
        cover = 0  # 当前能够覆盖的最远距离
        
        while i <= cover:  # 当前位置小于等于当前能够覆盖的最远距离时循环
            for i in range(i, cover+1):  # 遍历从当前位置到当前能够覆盖的最远距离之间的所有位置
                cover = max(nums[i]+i, cover)  # 更新当前能够覆盖的最远距离
                if cover >= len(nums)-1:  # 如果当前能够覆盖的最远距离达到或超过数组的最后一个位置,直接返回步数+1
                    return count+1
            count += 1  # 每一轮遍历结束后,步数+1

心得体会

对于方法二的判断一定要用len(nums)-1,因为如果循环到i==len(nums)-2时,i还不等于当前覆盖的最远距离,说明当前最远的覆盖距离一定可以达到终点,也就不需要再走一步啦 

1005.K次取反后最大化的数组和

思路

本题思路其实比较好想了,如何可以让数组和最大呢?

贪心的思路,局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。

局部最优可以推出全局最优。

那么如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让 数组和 达到最大。

那么又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值和可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),全局最优:整个 数组和 达到最大。

虽然这道题目大家做的时候,可能都不会去想什么贪心算法,一鼓作气,就AC了。

我这里其实是为了给大家展现出来 经常被大家忽略的贪心思路,这么一道简单题,就用了两次贪心!

那么本题的解题步骤为:

  • 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
  • 第二步:从前向后遍历,遇到负数将其变为正数,同时K--
  • 第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
  • 第四步:求和

方法一:贪心

class Solution:
    def largestSumAfterKNegations(self, A: List[int], K: int) -> int:
        A.sort(key=lambda x: abs(x), reverse=True)  # 第一步:按照绝对值降序排序数组A

        for i in range(len(A)):  # 第二步:执行K次取反操作
            if A[i] < 0 and K > 0:
                A[i] *= -1
                K -= 1

        if K % 2 == 1:  # 第三步:如果K还有剩余次数,将绝对值最小的元素取反
            A[-1] *= -1

        result = sum(A)  # 第四步:计算数组A的元素和
        return result

心得体会

对于最后的k判断,其实只要判断是不是为奇数就可以,因为偶数的话,对于数值的正负没有影响,只要是奇数就取反就行

标签:下标,游戏,覆盖,nums,II,prices,跳跃,步数,贪心
From: https://blog.csdn.net/m0_61698277/article/details/141176285

相关文章

  • [lnsyoj2256]消除游戏
    题意给定序列\(a\),每次可以选择其中长度\(>m\)且完全相同的一段并删除,并将序列的剩余部分拼合成一段;或在任意位置插入一个数。求最终将序列\(a\)清空的最小操作数sol很显然,本题的插入操作是为了能够使一段数能够凑够\(m\)个,因此我们可以只考虑删除操作,只在操作上下手脚......
  • leetcode面试经典150题-122. 买卖股票的最佳时机 II
    https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/?envType=study-plan-v2&envId=top-interview-150 gopackageleetcode150import"testing"funcTestMaxProfit2(t*testing.T){prices:=[]int{7,1,5,3,6,4}......
  • Modbus-Ascii注意事项
    1:消息以冒号 : 字符开头(ASCII表示为0x3A),以回车换行对 \r\n (ASCII表示为0x0D和0x0A)结尾;所有其他字段传输的数据所允许的十六进制表示字符为的 0-9、A-F,所以除了头和尾其他数据都是10进制的表现形式。2:数据每个8位的字节被拆分为两个ASCII字符进行发送,所以收到数据后两......
  • D42 2-SAT+二进制枚举 P3825 [NOI2017] 游戏
    视频链接: P3825[NOI2017]游戏-洛谷|计算机科学教育新生态(luogu.com.cn)//2-SAT+二进制枚举O(2^8*(n+m))#include<iostream>#include<cstring>#include<algorithm>usingnamespacestd;constintN=100005;inthead[N],to[N<<1],ne[N<<1],idx;......
  • Oracle数据库US7ASCII字符集中文乱码
    最近遇到一家客户的Oracle数据库,版本是11g,字符集是US7ASCII,当使用PL/SQLDeveloper工具插入和查询中文时都没问题,但是Java程序使用JDBC插入和查询中文时,中文乱码。比如'a中文b'通过JDBC查询出来的乱码是这样的'aᅱ￐ᅫᅣb'查询了一些资料,看到有网友通过这种方式解决了(只列出关键代码)。......
  • GameSalad-IOS-游戏开发学习手册-全-
    GameSaladIOS游戏开发学习手册(全)原文:LearnGameSaladforiOSGameDevelopmentforiPhone,iPad,andHTML5协议:CCBY-NC-SA4.0零、简介2007年,苹果推出了iPhone,彻底改变了我们的生活方式,但最重要的是iOS的诞生。今天,iOS被用于iPhone、iPad和iPodTouch。通过A......
  • 2D-游戏的物理引擎构建教程-全-
    2D游戏的物理引擎构建教程(全)原文:Buildinga2DGamePhysicsEngine协议:CCBY-NC-SA4.0一、2D游戏物理引擎开发简介电子补充材料本章的在线版本(doi:10.1007/978-1-4842-2583-7_1)包含补充材料,可供授权用户使用。物理引擎在许多类型的游戏中扮演着重要的角色。游戏对......
  • 【iis】URL重写可以引用原地址内容
    URL重写可以引用原地址内容如果iis没有URL重写模块,启用或关闭windows功能里也没有,在微软下载安装https://www.iis.net/downloads/microsoft/url-rewriteiisUrl重写入站空白规则,在匹配URL中“模式”输入域名"/"后的路径Path。匹配所有Path在匹配URL的“模式”中输入(.*),匹配一部分P......
  • 代码随想录day28 || 122 买卖最佳时机2,55 跳跃游戏,45 跳跃游戏2,1005 k次取反最大数组
    122买卖股票最佳时机2funcmaxProfit(prices[]int)int{ //思路,因为支持同一天买入卖出,所以利润最大应该是所有正利润的加总结果 varsumint fori:=1;i<len(prices);i++{ ifprices[i]-prices[i-1]>0{ sum+=prices[i]-prices[i-1] } } returns......
  • 游戏安全入门-扫雷分析&远程线程注入
    前言无论学习什么,首先,我们应该有个目标,那么入门windows游戏安全,脑海中浮现出来的一个游戏--扫雷,一款家喻户晓的游戏,虽然已经被大家分析的不能再透了,但是我觉得自己去分析一下还是极好的,把它作为一个小目标再好不过了。我们编写一个妙妙小工具,工具要求实现以下功能:时间暂停、修......