蓝桥杯备赛Day3(Python组)——动态规划

主要考点:线性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]

蓝桥杯备赛Day3(Python组)——动态规划_第1张图片

二、爬楼梯问题

相当于上面找零问题的简化版(不用找最小值)

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]等都可以由前面的推出来。

你可能感兴趣的:(蓝桥杯,职场和发展)