感觉不少贪心算法的题目都能用动态规划求解。
LeetCode 122 买卖股票的最佳时机II
题目链接:122. 买卖股票的最佳时机 II - 力扣(Leetcode)
这道题买卖股票不限制次数,贪心贪的就是股票正的价格差,局部极小值入手,价格即将下跌之前的极大值出手:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n < 2:
return 0 # 无法进行交易
idx, total = 0, 0
# 方法是找上升区间
while idx < n:
while idx < n - 1 and prices[idx] > prices[idx + 1]:
idx += 1
min_price = prices[idx]
while idx < n - 1 and prices[idx] < prices[idx + 1]:
idx += 1
total += prices[idx] - min_price
idx += 1
return total
只要一次遍历就能不断累积上升区间的最大利润,时间复杂度是O(n),空间复杂度是O(1)。而代码随想录讲解中将上升区间分解,直接在for循环遍历过程中累加差值为正的利润:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n < 2:
return 0 # 无法进行交易
total = 0
for i in range(1, n):
total += max(0, prices[i] - prices[i-1])
return total
LeetCode 55 跳跃游戏
因为题目只求是否能够到达最后一个下标,而不关心是怎么跳的,只需要判断在遍历过程中能跳的最远范围是否会覆盖最后一个下标:
class Solution:
def canJump(self, nums: List[int]) -> bool:
if len(nums) == 0:
return True
cover = 0
i = 0
while i < cover + 1:
cover = max(i + nums[i], cover) # 更新当前位置的覆盖范围
if cover >= len(nums) - 1:
return True
i += 1
return False
LeetCode 45 跳跃游戏II
题目链接:45. 跳跃游戏 II - 力扣(Leetcode)
这道题比跳跃游戏多了最小步数的限制条件,之前做题想用动态规划求解,但两重for循环直接超时了:
class Solution:
def jump(self, nums: List[int]) -> int:
n = len(nums)
dp = [0 for _ in range(n)] # dp数组含义:跳到下标为i处的最小跳跃次数
for i in range(1, n):
min_step = float('inf')
for j in range(i):
if nums[j] >= i - j:
# i在j的可跳跃范围
min_step = min(min_step, dp[j] + 1)
dp[i] = min_step
return dp[n-1]
# dp超时...
看讲解是同样以覆盖范围的角度解题,但除了当前的最大覆盖范围,还要考虑下一步,“如果移动下标达到了当前这一步的最大覆盖最远距离,还没有到终点的话,那么就必须再走一步来增加覆盖范围,直到覆盖范围覆盖了终点。”
class Solution:
def jump(self, nums: List[int]) -> int:
n = len(nums)
# 贪心
curdis, nextdis = 0, 0
ans = 0
for i in range(n):
nextdis = max(nextdis, i + nums[i])
if i == curdis:
if curdis < n - 1:
ans += 1
curdis = nextdis
if nextdis >= n - 1:
break # 无需再走
else:
break # 当前已经覆盖到n-1的位置
return ans
贪心真的太考验思维能力了,想到正确思路就能很简单。