题目链接 | 解题思路
此题规定了 1)一天只能持有一只股票;2)当天可以同时买卖股票。这样的规定下,最大的收益可以拆分为每个正收益的累计。只要记录每个间隔的收益,并计算正收益即可。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
count = 0
for i in range(len(prices) - 1):
if prices[i+1] - prices[i] > 0:
count += prices[i+1] - prices[i]
return count
题目链接 | 解题思路
“在每一个节点跳几步”是一个类似回溯的思路,对于本体来说过于复杂,也没有必要。
本题重点是能意识到动态的覆盖范围:如果所有节点跳跃的覆盖范围能够包括最后一个节点,则可以到达;反之不能。意识到这点之后,就不必具体思考跳跃的方式了。
局部最优:在每个节点更新当前的最大覆盖范围
全局最优:得到最终的最大覆盖范围(并判断是否覆盖终点)
class Solution:
def canJump(self, nums: List[int]) -> bool:
cover = 0 # include the rightmost element
i = 0
while i <= cover:
cover = max(cover, nums[i] + i)
if cover >= len(nums) - 1:
return True
i += 1
return False
本题的动态规划和贪心算法思路差不多。dp[i]
记录的是当前节点能够到达的最远节点,通过更新 dp 数组判断能否到达最后一个节点。
注意,判断的依据应当是倒数第二个节点:dp[-2] >= len(nums) - 1
,但如果能够到达,dp[-1] >= len(nums) - 1
也一定成立,反之则不成立,所以最后写的判断条件是 dp[-1] >= len(nums) - 1
。
class Solution:
def canJump(self, nums: List[int]) -> bool:
dp = [0] * len(nums)
dp[0] = nums[0]
for i in range(1, len(nums)):
if dp[i-1] < i:
dp[i] = dp[i-1]
else:
dp[i] = max(dp[i-1], i + nums[i])
return dp[-1] >= len(nums) - 1
题目链接 | 解题思路
本题依然要依靠最大覆盖范围来进行贪心,不同的是,上一题中关注的是最大覆盖范围,而本题关注的是增加覆盖范围所用的移动次数。
有一点很关键:对于 i
,[i+1, i + nums[i]]
内的节点都可以仅通过一次移动到达。例如,nums = [2, 3, 1]
,从 idx=0
的节点进行一次移动可以到达 idx=1, idx=2
的那些节点。
所以,本题的局部最优是,对于每一次移动,都尽可能增加当前最大覆盖范围。如果当前最大覆盖范围没有包括终点,那就必须进行下一次移动,才有可能到达终点。当最大覆盖范围包括了终点时,就用了最少的移动次数(全局最优)。
在当前步数的覆盖范围 curr_cover
内,不断更新“如果多走一步”的覆盖范围 next_cover
。如果发现走到当前覆盖范围的终点,却依然没有遇到全剧终点,那就只能选择多移动一次,在新的覆盖范围内查看能否覆盖全剧终点。
class Solution:
def jump(self, nums: List[int]) -> int:
num_jumps = 0
curr_cover = next_cover = 0
for i in range(len(nums)):
next_cover = max(next_cover, i + nums[i])
if i == curr_cover:
if curr_cover >= len(nums) - 1:
break
num_jumps += 1
curr_cover = next_cover
return num_jumps
dp[i]
记录的是到达 idx=i
的最少移动次数。对于 idx=i
的元素来说,我们知道多移动一次就可以到达 idx = [i+1, i+nums[i]]
的元素。所以对于每个元素,都可以更新自己覆盖的节点的 dp。
class Solution:
def jump(self, nums: List[int]) -> int:
dp = [float('inf')] * len(nums)
dp[0] = 0
for i in range(1, len(nums)):
for j in range(i, i + nums[i-1]):
if j < len(nums):
dp[j] = min(dp[i-1] + 1, dp[j])
if j == len(nums) - 1:
return dp[j]
return dp[-1]
以上写法的时间复杂度很高,主要是对于同一个节点 i,dp[i]
可能会被访问很多次。实际只需要记录第一次访问该节点时的步数即可,时间复杂度可以降到 O ( n ) O(n) O(n)。
class Solution:
def jump(self, nums: List[int]) -> int:
dp = [float('inf')] * len(nums)
dp[0] = 0
next_start = 1
for i in range(1, len(nums)):
for j in range(next_start, i + nums[i-1]):
if j < len(nums):
dp[j] = min(dp[i-1] + 1, dp[j])
if j == len(nums) - 1:
return dp[j]
next_start = i + nums[i-1]
return dp[-1]