您现在的位置是:首页 >技术杂谈 >算法记录 | Day32 贪心算法网站首页技术杂谈

算法记录 | Day32 贪心算法

是菜鸡小小陈啊 2023-05-26 08:00:03
简介算法记录 | Day32 贪心算法

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

贪心算法

思路:

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

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

如图:

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

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

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

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

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

动态规划

  1. 状态定义

状态表示:
状态方程 = { d p [ i ] [ 0 ] = 表示第 i 天交易完后,手上没有股票时的最大利润, d p [ i ] [ 1 ] = 表示第 i 天交易完后,手上持有股票时的最大利润。 状态方程 = egin{cases} dp[i][0] =表示第 i 天交易完后,手上 没有 股票时的最大利润,\ dp[i][1] =表示第 i 天交易完后,手上 持有 股票时的最大利润。 end{cases} 状态方程={dp[i][0]=表示第i天交易完后,手上没有股票时的最大利润,dp[i][1]=表示第i天交易完后,手上持有股票时的最大利润。

  1. 状态转移

状态转移方程为:

{ d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) egin{cases} dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]) \ dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i]) end{cases} {dp[i][0]=max(dp[i1][0],dp[i1][1]+prices[i])dp[i][1]=max(dp[i1][1],dp[i1][0]prices[i])

  1. 初始化

对第一天(i=0)的状态进行初始化:

dp[0] [0]: 第一天手上无股票,也没有购入股票。

dp[0] [1]: 第一天手上持有股票说明在当天购入了股票,花费 prices[0]prices[0]prices[0],即所获利润为 −prices[0]-prices[0]−prices[0]。

  1. 执行结果

遍历全部的日期i,则最后一天手上没有股票时的状态值dp[n-1] [0]即为我们能够获得的最大利润。

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        n = len(prices)
        # dp[i][0]:第i天结束后,手上没有股票的最大利润(第i天卖出股票 )
        # dp[i][1]:第i天结束后,手术持有股票的最大利润(第i天买入股票)
        dp = [[0]*2  for _ in range(n)]

        # 初始化
        dp[0][0] = 0
        dp[0][1] = -prices[0]

        for i in range(1,n):
            # dp[i][0]:max(前一天没有股票,前一天持有股票+当天卖出)
            dp[i][0] = max(dp[i-1][0],dp[i-1][1]+prices[i]) 
            # dp[i][1]:max(前一天持有股票,前一天没有股票+当天买入)
            dp[i][1] = max(dp[i-1][1],dp[i-1][0]-prices[i])
        return max(dp[n-1][0],dp[n-1][1])
        # return dp[n - 1][0]

55.跳跃游戏

思路:

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

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

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

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

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

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

具体步骤:

  1. 初始化能到达的最远位置max_i为 0。
  2. 遍历数组 nums。
  3. 如果能到达当前位置,即 max_i<i,并且当前位置 + 当前位置最大跳跃长度 > 能到达的最远位置,即 i + nums[i] > max_i,则更新max_i。
  4. 遍历完数组,最后比较能到达的最远位置 max_i和数组最远距离 size - 1 的关系
class Solution:
    def canJump(self, nums: List[int]) -> bool:
        size = len(nums)
        max_i = 0
        for i in range(size-1):
            if max_i >=i and i + nums[i] > max_i:
                max_i = i + nums[i]
        return max_i >= size -1 

45.跳跃游戏II

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

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

思路:

  1. 当前所能达到的最远位置 end,下一步所能跳到的最远位置 max_pos,最少跳跃次数 setps
  2. 遍历数组 nums 的前 len(nums) - 1个元素:
  3. 每次更新第 i位置下一步所能跳到的最远位置 max_pos
  4. 如果索引 i到达了 end 边界,则:更新 end为新的当前位置 max_pos,并令步数steps+1
  5. 最终返回跳跃次数 steps。
class Solution:
    def jump(self, nums: List[int]) -> int:
        end = 0
        max_pos = 0
        step = 0
        for i in range(len(nums)-1):
            max_pos  =  max(i + nums[i],max_pos)
            if i == end:
                end = max_pos
                step += 1
        return  step
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。