第一题: LeetCode55:跳跃游戏
方法一: 从左到右
我自己认为,所谓动态规划,重点是要推出某些数学公式一样的结论! 不必拘泥于什么动态转移方程, 能推出某些数学结论, 就按照这些来
对于从左到右搜索:
假如有 3 1 0 2 … 起始数字为3
那么它该怎么走: 肯定是跳到2啊, 因为在接下来的3个搜索空间中, 2到达的距离最远
那么思考一个问题: 每次跳最远, 一定是最优的吗
答案是一定, 以为跳到最远的,可拥有的选择一定比不跳到最远后接下来的搜索空间大或想等
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums: return False
if len(nums) == 1: return True
i = 0
while i < len(nums)-1:
far = i
nexti = i
for step in range(1, nums[i]+1):
if i + step > len(nums) - 2: return True
remote = i+step+nums[i+step]
if remote > far:
far = remote
nexti = i + step
if nexti == i: return False
i = nexti
方法二: 从右到左
def canJump(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
j = len(nums) - 1
for i in range(len(nums)-2, -1, -1):
if nums[i] >= j - i:
j = i
return j == 0
其中,
j
j
是能触摸到的最左边的位置
其实就是从右到左, 如果能从这个结点跳过来就跳过去
当 能触摸到0的时候代表可行
第二题: LeetCode62: 不同路径
法一: 排列组合
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
def fact(n, res):
if n == 1 or n == 0: return res
return fact(n-1, res*n)
return fact(m+n-2, 1) // fact(n-1, 1) // fact(m-1, 1)
注意是 Cn−1m+n−2 C m + n − 2 n − 1
法二: 动态规划
先初始化
m∗n
m
∗
n
的矩阵,每个元素为1
对每个格子, 它的路径数为左边加上上面
def uniquePaths(self, m, n):
"""
:type m: int
:type n: int
:rtype: int
"""
dp = [[1]*n]*m
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i][j-1] + dp[i-1][j]
return dp[m-1][n-1]
第三题: LeetCode322: 零钱兑换
def coinChange(self, coins, amount):
"""
:type coins: List[int]
:type amount: int
:rtype: int
"""
if not amount: return 0
if not coins: return -1
visited = [False] * (amount + 1)
coins.sort(reverse=True)
pre, suf = [0], []
cnt = 0
while pre:
cnt += 1
for num in pre:
for coin in coins:
if num + coin == amount:
return cnt
elif num + coin > amount:
continue
else:
if not visited[num+coin]:
visited[num + coin] = True
suf.append(num+coin)
pre, suf = suf, []
return -1
第四题: LeetCode300:最长上升子序列
法一: 直接上DP
时间复杂度为 O(n2) O ( n 2 ) , 额外空间复杂度为 O(n) O ( n )
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) < 2: return len(nums)
dp = [1] * len(nums)
imax = 1
for i in range(1, len(nums)):
cnt = 0
for j in range(i):
if nums[j] < nums[i] and dp[j] > cnt:
cnt = dp[j]
dp[i] = cnt + 1
if dp[i] > imax: imax = dp[i]
return imax
法二: 贪心策略:
对于每一个已经找到的上升序列S
如果接下来的数x比S最后一个元素大, 那么就将x加在S后面
如果x比S最后一个元素小, 那么找到S中第一个 < x的坐标i, 将mins[i+1]替换为x
时间复杂度为
O(nlgn)
O
(
n
l
g
n
)
, 额外空间复杂度为
O(k)
O
(
k
)
,
k
k
<script type="math/tex" id="MathJax-Element-11">k</script>为最长上升子序列的长度
def searchFirstLess(self, nums, target):
if not nums or nums[0] >= target: return -1
left, right = 0, len(nums)
while left < right:
mid = (right - left) // 2 + left
if nums[mid] >= target:
right = mid
else:
left = mid + 1
return left - 1
def lengthOfLIS(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) < 2: return len(nums)
# 贪心策略
mins = [nums[0]]
for i in range(1, len(nums)):
if nums[i] > mins[-1]:
mins.append(nums[i])
else:
pos = self.searchFirstLess(mins, nums[i])
if pos == -1:
mins[0] = nums[i]
else:
mins[pos+1] = nums[i]
return len(mins)