动态规划
动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。动态规划经分解得到子问题往往不是互相独立的,有些子问题被重复计算了很多次。因此若保存已解决的子问题的答案,而在需要时再找出已求得的答案,可避免大量的重复计算,节省时间。
解动态规划问题的步骤:
1.找出状态转移方程
2.设计自顶而下的递归算法 (Top-down approach)
3.改写成自底而上的迭代算法(Bottom-up approach)
题目在这里就不过多介绍了
递归(调用函数自身称为递归)
if n == 1:
return 1
elif n == 2:
return 2
else:
s1 = self.climbStairs(n-1)
s2 = self.climbStairs(n-2)
return s1+s2
Time Limit Exceeded
class Solution:
def climbStairs(self, n: int) -> int:
if n<4:return n
result=[1,2,3]
for i in range(3,n):
result.append(result[i-1]+result[i-2])
return result[n-1]
Runtime: 36 ms, faster than 55.93% of Python3 online submissions for Climbing Stairs.
Memory Usage: 13.1 MB, less than 5.18% of Python3 online submissions for Climbing Stairs.
思路解析:
主要掌握的关键点是将问题拆分成可递归的子问题。当前楼梯数为n级,具有的走法是n-1级楼梯的走法与n-2级楼梯的走法之和(限制一次只能走一级或两级楼梯)
则状态转移方程很明了
ps.递归的好处是程序结构简单逻辑明了,缺点是计算速度慢,空间复杂度高;
更多的情况下考虑将递归变为迭代可以有效地提高计算速度
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
if n == 1 or n == 0:
return 1
i = 2
temp1 = 1
temp2 = 1
while i <= n:
temp1 += temp2
if i == n:
return temp1
i += 1
temp2 += temp1
if i == n:
return temp2
i += 1
运行时间和所占内存和code 1差不多;
思路解析: 欲求得第i层阶梯处的走法总数,仅需知道第i-1层和i-2层的走法,两项相加即可得到i层。代码中temp1和temp2分别表示i-1和i-2层的走法数,其实就是迭代的思想(具体的迭代过程画图走个三四布即可理解)
class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if nums==[]:
return 0
if len(nums)==1:
return max(nums)
dp = [0]*len(nums)
dp[0] = nums[0]
dp[1] = max(nums[1],nums[0])
for i in range(2,len(nums)):
dp[i] = max(dp[i-1],dp[i-2]+nums[i])
return dp[len(nums)-1]
思路:dp[i]表示从0-i户可以打劫到的最大钱数。则有dp[i] = max(dp[i-1],dp[i-2]+nums[i])。第(i-1)户打劫到的最大钱数+不打劫第i户,与第(i- 2)户打劫的最大钱数+打劫第i户,两者中的最大值
class Solution:
def maxSubArray(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
length=len(nums)
for i in range(1,length):
#当前值的大小与前面的值之和比较,若当前值更大,则取当前值,舍弃前面的值之和
subMaxSum=max(nums[i]+nums[i-1],nums[i])
nums[i]=subMaxSum#将当前和最大的赋给nums[i],新的nums存储的为和值
return max(nums)
题目:现有金矿数n,人数w,每个金矿对应的金矿数量为g[n],需要的人数为p[n]
现寻求最佳的人数分配方案使得最终获得的金矿数目最多
解题思路: 状态转移方程dp[n,w]=max(dp[n-1,w],dp[n-1,w-p[n-1]]+g[n-1])
n座金矿,w个人情况下的最佳金矿数 = max{(不挖第n座金矿时的人员配置下的最大价值,挖第n座金矿时剩余人员配置下的最大价值+第n座金矿的金矿数目)}
注意点:当j
def mine(n,w,g=[],p=[]):
arr=[0]*w
for i in range(w):
if i+1>=p[0]:
arr[i]=g[0]
res=copy.deepcopy(arr)
print(res)
####只有第一座金矿时的最大价值
####res代表有i-1座金矿时的最大价值
####arr代表有i座金矿时的最大价值
for i in range(1,n):
for j in range(w):
if j+1
题目: 钱总数amount,现有零钱列表coins,寻找零钱组合使得使用到的零钱数目最少(使用数字可重复)
思路: dp[i]表示amount为i的时候的最少次数
状态转移方程:dp[i]=min(dp[i-coin]) for coin in coins
关键点: dp=[o] + [float(‘inf’)] * amount 排除在选择min时候的干扰
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
MAX=float('inf')
dp=[0]+[MAX]*amount
for i in range(1,amount+1):
for coin in coins:
if i-coin>=0:
dp[i]=min(dp[i],dp[i-coin]+1)
if dp[-1]==float('inf'):
return -1
else:
return dp[-1]
题目: 三角形数堆,寻找一条路径使得从上到下的路径和最小(其中有路径临近选择约束)
思路: 状态转移方程dp[i][j]=min(dp[i-1][j-1]+triangle[i][j] , dp[i-1][j]+triangle[i][j])
注意点:边界情况 j0和ji的情况
class Solution:
def minimumTotal(self, triangle: List[List[int]]) -> int:
lens=len(triangle)
dp=[0]*lens
dp[0]=triangle[0]
for i in range(1,lens):
for j in range(i+1):
if j==0:
dp[i]=[dp[i-1][j]+triangle[i][j]]
elif j==i:
dp[i].append(dp[i-1][j-1]+triangle[i][j])
else:
dp[i].append(min((dp[i-1][j-1]+triangle[i][j]),(dp[i-1][j]
+triangle[i][j])))
return min(dp[-1])
题目: Alice和Bob轮流玩游戏,选定数字N,在尽最大可能的前提下,选择x满足两个条件:1、N%x==0 2、0
如果一方无法满足当前两条件则失败
如果Alice能获得胜利则返回True,否则Bob胜利返回False
思路: 动态规划 当前所在i ,for j in rang(1,i),若存在i%j==0且dp[i-j]==False及存在满足取余条件的情况下有任一个取值使得dp[i-j]即对方失败,则本方胜利;否则对方成功本方失败;
同时有一个小捷径;如果i=2t即i是2的倍数 并且dp[t]==False,那么i为True(因为i%t ==0是接下来的j的for循环的一个特殊案例)
class Solution:
def divisorGame(self, N: int) -> bool:
dp=[False]*(N+1)
for i in range(2,N+1):
if i%2==0 and dp[int(i/2)]==False:
dp[i]=True
else:
for j in range(1,i):
if i%j==0:
if dp[i-j]==False:
dp[i]=True
break
return dp[N]