代码随想录算法训练营第二十八天 |贪心算法part02
122 买卖股票的最佳时机II
如果想到其实最终利润是可以分解的,那么本题就很容易了!
如何分解呢?
假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。
相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。
此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑!
局部最优:收集每天的正利润,全局最优:求得最大利润。
prices = [7,1,5,3,6,4]
max_ = 0
result = []
for i in range(len(prices)-1):
diff = prices[i+1] - prices[i]
result.append(diff)
sum_ = 0
for i in result:
if i>0:
sum_ += i
print(sum_)
result = 0
for i in range(1, len(prices)):
result += max(prices[i] - prices[i - 1], 0)
return result
55 跳跃游戏
当前位置元素如果是 3,我究竟是跳一步呢,还是两步呢,还是三步呢,究竟跳几步才是最优呢?
其实跳几步无所谓,关键在于可跳的覆盖范围!
不一定非要明确一次究竟跳几步,每次取最大的跳跃步数,这个就是可以跳跃的覆盖范围。
这个范围内,别管是怎么跳的,反正一定可以跳过来。
那么这个问题就转化为跳跃覆盖范围究竟可不可以覆盖到终点!
每次移动取最大跳跃步数(得到最大的覆盖范围),每移动一个单位,就更新最大覆盖范围。
贪心算法局部最优解:每次取最大跳跃步数(取最大覆盖范围) 整体最优解:最后得到整体最大覆盖范围,看是否能到终点。
局部最优推出全局最优,找不出反例,试试贪心!
if len(nums) == 1:
return True
cover = 0
i = 0
while i <= cover: # cover是一直变化的,所以如果用for来确定i的上界,不合适。cover代表着i的上界
cover = max(nums[i] + i ,cover)
if cover >= len(nums)-1:
return True
i += 1
return False
基于当前最远可到达位置判断
## 基于当前最远可到达位置判断
class Solution:
def canJump(self, nums: List[int]) -> bool:
far = nums[0]
for i in range(len(nums)):
# 要考虑两个情况
# 1. i <= far - 表示 当前位置i 可以到达
# 2. i > far - 表示 当前位置i 无法到达
if i > far:
return False
far = max(far, nums[i]+i)
# 如果循环正常结束,表示最后一个位置也可以到达,否则会在中途直接退出
# 关键点在于,要想明白其实列表中的每个位置都是需要验证能否到达的
return True
45 跳跃游戏II
真正解题的时候,要从覆盖范围出发,不管怎么跳,覆盖范围内一定是可以跳到的,以最小的步数增加覆盖范围,覆盖范围一旦覆盖了终点,得到的就是最少步数!
这里需要统计两个覆盖范围,当前这一步的最大覆盖和下一步最大覆盖。
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
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
1005 K次取反后最大化的数组和
暴力解法:
for i in range(k):
number = min(nums)
idx = nums.index(number)
nums[idx] = -nums[idx]
return sum(nums)
贪心的思路,
局部最优:让绝对值大的负数变为正数,当前数值达到最大,
整体最优:整个数组和达到最大。
那么如果将负数都转变为正数了,K依然大于0,此时的问题是一个有序正整数序列,如何转变K次正负,让 数组和 达到最大。
那么又是一个贪心:局部最优:只找数值最小的正整数进行反转,当前数值和可以达到最大(例如正整数数组{5, 3, 1},反转1 得到-1 比 反转5得到的-5 大多了),全局最优:整个 数组和 达到最大。
nums.sort(key = lambda x:abs(x),reverse = True)
for i in range(len(nums)):
if k > 0 and nums[i] < 0 :
nums[i] *= -1
k -= 1
if k % 2 == 1:
nums[-1] *= -1
return sum(nums)