主要考点:线性DP、背包DP、记忆化搜索
一、找零兑换问题
1. 递归解法
def recMC(coinValuelist,change):
minCoins = change # 最少零钱个数
if change in coinValuelist: # 递归边界是四种单位零钱
return 1
else:
for i in [c for c in coinValuelist if c <= change]: # 遍历每一种小于现在数额的零钱
numCoins = 1 + recMC(coinValuelist, change-i) #每次遍历numCoins就会++1
if numCoins < minCoins: # 递归完成,找到numCoins后才会进入这个if循环
minCoins = numCoins
return minCoins
print(recMC([1,5,10,25],63))
def recDC(coinValuelist,change,knownResults): # 添加一个记忆数组
minCoins = change
if change in coinValuelist: # 递归边界
knownResults[change] = 1 #不是设置而是初始化
return 1
elif knownResults[change] > 0: # 记忆数组中有,直接用最优解
return knownResults[change]
else:
for i in [c for c in coinValuelist if c <= change]:
numCoins = 1 + recMC(coinValuelist, change-i)
if numCoins < minCoins:
# 找到最优解,记录到表中
minCoins = numCoins
knownResults[change] = minCoins
return minCoins
print(recDC([1,5,10,25],63,[0]*64)) # 0~63的所有值初始化为0
【说明】
1. 在递归过程中的三种情况
a. 递归边界(change为基本零钱单位中的)
顺便初始化记忆数组
返回1
b. 在记忆数组中找到
c. 其他
对基本零钱单位中的每一个进行循环递归
递归结束后如果硬币个数(递归次数)更小就更新,并更新记忆数组
2. 递推公式含义
numCoins = 1 + recMC(coinValuelist, change-i)
numCoins:存储硬币使用个数,在本题中就是递归次数,每递归一次相当于用一个硬币
1+:使得每次递归numCoins ++1
recMC中change-i:每次递归减去一个使用的硬币面额
2. dp解法
# 动态规划问题在找问题的最优解时,将问题的最优解拆解成包含了更小规模子问题的最优解
def dpMakeChange(coinValueList, change, minCoins):
for cents in range(1, change+1): #遍历minCoins列表里的每个索引,【顺序】为从前向后(前面为已知后面为未知,由已知推未知)
# 1. 【初始化】每个索引为最大值(全部使用1元硬币)
coinCount = cents
# 2. 递推找子问题中的最优解
for j in [c for c in coinValueList if c <= cents]:
if minCoins[cents - j] + 1 < coinCount:
coinCount = minCoins[cents - j] + 1
minCoins[cents] = coinCount
return minCoins[change]
二、爬楼梯问题
相当于上面找零问题的简化版(不用找最小值)
def ClimbStairs(StairValueList, n, dp):
sum = 0
for i in StairValueList:
dp[i] = sum+1
sum += dp[i]
for stairs in range(StairValueList[-1]+1, n+1):
for i in StairValueList:
dp[stairs] += dp[stairs-i]
return dp[n]
print(ClimbStairs([1,2],6,[0]*7))
三、使用最小花费爬楼梯
def ClimbStairs(cost):
dp = [0]*(len(cost)+1)
for i in range (2,len(cost)+1):
sum = 0
for j in range(i):
sum += cost[j]
for j in [1,2]:
if dp[i-j]+cost[i-j] < sum:
sum = dp[i-j]+cost[i-j]
dp[i] = sum
print(dp)
return dp[-1]
print(ClimbStairs([1, 100, 1, 1, 1, 100, 1, 1, 100, 1]))
上面这个解法中sum的作用是存储跨一阶楼梯或两阶楼梯时花费的力气并找到最小值(而且相当于在初始化时把sum设置为了可能的最大值)(这是因为参考了上面找零问题的思路),但是由于只有两种情况,所以可以直接这么写
def ClimbStairs(cost):
dp = [0]*(len(cost)+1)
for i in range (2,len(cost)+1):
dp[i] = min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
print(dp)
return dp[-1]
print(ClimbStairs([1, 100, 1, 1, 1, 100, 1, 1, 100, 1]))
注意初始化dp数组时,重点在设置dp[0] = dp[1] = 0。因为由递推公式可知后面的dp[2]等都可以由前面的推出来。