蓝桥杯Python赛道备赛——Day8:动态规划(基础)案例分析

   本博客就上一期中讨论的蓝桥杯动态规划基础问题(包括:递推、记忆化搜索、最长公共子序列和最长上升子序列),给出了六个常见的案例问题。

   每一个问题都给出了其求解方法的示例代码,以供低年级师弟师妹们学习和练习。如有不懂,欢迎在评论区提问。

   前序知识:
(1)Python基础语法
(2)Day1:基础算法
(3)Day7:动态规划(基础)


动态规划(基础)案例分析

    • 一、递推应用:爬楼梯问题
    • 二、递推应用:零钱兑换
    • 三、记忆化搜索应用:网格路径计数
    • 四、LCS应用:DNA序列比对
    • 五、LIS应用:航班预订系统
    • 六、LIS优化应用:俄罗斯套娃信封

一、递推应用:爬楼梯问题

  • 问题描述:每次可以爬1或2阶台阶,到达n阶有多少种方法。
  • 应用场景:运动健康类APP计算运动方案。
def climb_stairs(n):
    # dp[i]表示到达第i阶的方法数
    dp = [0]*(n+1)
    dp[0] = 1  # 初始状态:地面只有1种方法(不动)
    dp[1] = 1  # 第一阶只有1种方法
    
    for i in range(2, n+1):
        # 状态转移:可以从i-1阶爬1步,或i-2阶爬2步
        dp[i] = dp[i-1] + dp[i-2]
    
    return dp[n]

print(climb_stairs(5))  # 输出:8(5阶楼梯共有8种走法)

二、递推应用:零钱兑换

  • 问题描述:用给定面额硬币凑出指定金额所需最少硬币数。
  • 应用场景:自动售货机找零系统。
def coin_change(coins, amount):
    # dp[i]表示凑出金额i需要的最小硬币数
    dp = [float('inf')] * (amount+1)
    dp[0] = 0  # 金额0不需要硬币
    
    for i in range(1, amount+1):
        for coin in coins:
            if i >= coin:
                # 状态转移:考虑使用当前硬币的情况
                dp[i] = min(dp[i], dp[i-coin]+1)
    
    return dp[amount] if dp[amount] != float('inf') else -1

print(coin_change([1,2,5], 11))  
# 表示:共有硬币面额1、2和5,凑出11元钱。
# 输出:3(5+5+1)

三、记忆化搜索应用:网格路径计数

  • 问题描述:机器人从左上角到右下角的路径数(含障碍物)。
  • 应用场景:物流机器人路径规划。
def unique_paths(grid):
    memo = {}
    m, n = len(grid), len(grid[0])
    
    def dfs(i, j):
        # 遇到障碍物或越界返回0
        if i >= m or j >= n or grid[i][j] == 1:
            return 0
        # 到达终点返回1
        if i == m-1 and j == n-1:
            return 1
        # 查询缓存
        if (i,j) in memo:
            return memo[(i,j)]
            
        # 递归计算并缓存结果
        # 可以选择向下走(dfs(i+1, j))或向右走(dfs(i, j+1))
        # 两者的结果加起来,得到从当前位置到终点的所有路径数
        memo[(i,j)] = dfs(i+1,j) + dfs(i,j+1)
        
        return memo[(i,j)]
    
    return dfs(0,0)

# 测试:0表示可通行,1表示障碍物
grid = [
    [0,0,0,0],
    [0,1,0,0],
    [0,0,0,0],
    [0,0,0,0]
]
print(unique_paths(grid))  # 输出:8

四、LCS应用:DNA序列比对

  • 问题描述:计算两个DNA序列的相似度。
  • 应用场景:生物信息学基因分析。
def dna_similarity(dna1, dna2):
    m, n = len(dna1), len(dna2) # m 和 n 分别表示 DNA 序列 dna1 和 dna2 的长度
    
    # dp[i][j] 表示 dna1 的前 i 个字符与 dna2 的前 j 个字符的最长公共子序列的长度
    # 这里多一行和多一列用于处理边界情况(空字符串的情况)
    dp = [[0]*(n+1) for _ in range(m+1)]
    
    
    for i in range(1, m+1):
        for j in range(1, n+1):
        	# 匹配情况:如果 dna1[i-1] 等于 dna2[j-1],则说明当前两个字符相同
        	# 可以将公共子序列的长度加 1。即:dp[i][j] = dp[i-1][j-1] + 1
            if dna1[i-1] == dna2[j-1]:
                dp[i][j] = dp[i-1][j-1] + 1
            # 不匹配情况:如果两个字符不同,则无法延长当前的公共子序列
            # 所以下一位置的公共子序列长度为 dp[i-1][j] 或 dp[i][j-1] 的最大值
            else:
                dp[i][j] = max(dp[i-1][j], dp[i][j-1])
    
    # 计算相似度比例
    return dp[m][n] / max(m, n)

seq1 = "ATGCGAT"
seq2 = "ATCGAAT"
print(f"相似度:{dna_similarity(seq1, seq2):.4f}")  
# 输出:相似度:0.8571

五、LIS应用:航班预订系统

  • 问题描述:选择最多的不冲突航班(类似最长递增子序列变种)。
  • 应用场景:航空公司最优航班选择。
def max_flights(flights):
    # 按起飞时间排序
    flights.sort(key=lambda x: x[0])
    n = len(flights)
    dp = [1]*n
    
    for i in range(n):
        for j in range(i):
            # 当前航班起飞时间 >= 前序航班降落时间
            if flights[i][0] >= flights[j][1]:
                dp[i] = max(dp[i], dp[j]+1)
    
    return max(dp) if n > 0 else 0

# 每个航班格式:[起飞时间, 降落时间]
flights = [[1,3], [2,5], [4,7], [6,9]]
print(max_flights(flights))  # 输出:2(选择1-3,4-7)

六、LIS优化应用:俄罗斯套娃信封

  • 问题描述:要求在给定一组信封的尺寸中,找出最多可以嵌套的信封数量(二维LIS问题)。
  • 应用场景:物流仓储优化。
def max_envelopes(envs):
    # 按宽度升序,高度降序排列
    envs.sort(key=lambda x: (x[0], -x[1]))
    
    # 转换为寻找高度的LIS
    heights = [e[1] for e in envs]
    tails = []
    
    for h in heights:
        # 二分查找插入位置
        left, right = 0, len(tails)
        while left < right:
            mid = (left+right) // 2
            if tails[mid] < h:
                left = mid + 1
            else:
                right = mid
        # 更新tails数组
        if left == len(tails):
            tails.append(h)
        else:
            tails[left] = h
    
    return len(tails)

envelopes = [[5,4],[6,4],[6,7],[2,3]] # 存放的是信封的宽和高
print(max_envelopes(envelopes))  # 输出:3([2,3]=>[5,4]=>[6,7])

你可能感兴趣的:(蓝桥杯备赛,蓝桥杯,python,动态规划)