• 动态规划的算法题以及其python实现


    动态规划(Dynamic Programming)

    动态规划(Dynamic Programming)是一种常用的算法设计技术,用于解决具有最优子结构性质和重叠子问题的问题。它通过将原问题分解为若干个子问题,并先求解子问题的最优解,然后利用子问题的最优解构建原问题的最优解。

    动态规划算法通常包括以下几个关键步骤

    1. 定义状态:将原问题划分为若干个子问题,并定义合适的状态表示问题的特征和限制条件。

    2. 确定状态转移方程:通过分析问题的特点和限制条件,找出子问题之间的关系,建立状态之间的转移方程。这个方程描述了问题的最优解与子问题的最优解之间的关系。

    3. 初始化边界条件:确定初始阶段的状态值,以及初始阶段到下一阶段的状态转移。

    4. 通过状态转移方程求解:根据状态转移方程,从初始阶段开始,逐个计算每个阶段的状态值,直到求解出最终的目标状态。

    5. 根据最终状态得出结果:根据最终的目标状态,得出问题的最优解。

    动态规划算法通常使用动态规划表或一维/二维数组来存储子问题的解,以减少重复计算,提高算法的效率。

    动态规划算法可以解决很多问题,如最短路径问题、背包问题、最长公共子序列问题等。它在实际应用中具有广泛的应用,能够有效地解决一些复杂的优化问题。

    常见算法题

    1.最短路径问题

    最短路径问题是动态规划算法的一个经典应用,其中最著名的算法是Dijkstra算法。以下是Dijkstra算法的Python实现示例:

    import heapq
    
    def dijkstra(graph, start):
        # 初始化距离字典,用于存储起点到各个顶点的最短距离
        distances = {vertex: float('inf') for vertex in graph}
        distances[start] = 0
    
        # 初始化优先队列,用于选择距离最短的顶点
        heap = [(0, start)]
    
        while heap:
            # 弹出当前距离最短的顶点
            current_distance, current_vertex = heapq.heappop(heap)
    
            # 如果当前距离大于已记录的最短距离,则跳过
            if current_distance > distances[current_vertex]:
                continue
    
            # 遍历当前顶点的邻居顶点
            for neighbor, weight in graph[current_vertex].items():
                distance = current_distance + weight
    
                # 如果通过当前顶点到达邻居顶点的距离更短,则更新最短距离
                if distance < distances[neighbor]:
                    distances[neighbor] = distance
                    heapq.heappush(heap, (distance, neighbor))
    
        return distances
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    使用示例:

    # 定义有向加权图
    graph = {
        'A': {'B': 5, 'C': 2},
        'B': {'D': 4, 'E': 2},
        'C': {'B': 8, 'E': 7},
        'D': {'E': 6, 'F': 3},
        'E': {'F': 1},
        'F': {}
    }
    
    start_vertex = 'A'
    distances = dijkstra(graph, start_vertex)
    
    print(f"从顶点 {start_vertex} 出发到各个顶点的最短距离:")
    for vertex, distance in distances.items():
        print(f"顶点 {vertex}: {distance}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    输出结果:

    从顶点 A 出发到各个顶点的最短距离:
    顶点 A: 0
    顶点 B: 5
    顶点 C: 2
    顶点 D: 9
    顶点 E: 7
    顶点 F: 8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    以上是Dijkstra算法的Python实现示例,用于求解最短路径问题。该算法通过不断选择距离最短的顶点,更新起点到各个顶点的最短距离,直到找到最短路径。

    2.背包问题

    背包问题是动态规划算法的经典应用之一。以下是背包问题的动态规划算法的Python实现示例:

    def knapsack(weights, values, max_weight):
        n = len(weights)
        dp = [[0] * (max_weight + 1) for _ in range(n + 1)]
        
        for i in range(1, n + 1):
            for j in range(1, max_weight + 1):
                if weights[i - 1] <= j:
                    dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]])
                else:
                    dp[i][j] = dp[i - 1][j]
        
        return dp[n][max_weight]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    使用示例:

    weights = [2, 3, 4, 5]
    values = [3, 4, 5, 6]
    max_weight = 8
    
    max_value = knapsack(weights, values, max_weight)
    print("背包能容纳的最大价值为:", max_value)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    输出结果:

    背包能容纳的最大价值为: 10
    
    • 1

    以上代码实现了一个动态规划算法来解决背包问题。

    在算法中,我们使用一个二维数组dp来保存子问题的解,
    其中dp[i][j]表示前i个物品在背包容量为j时的最大价值。

    通过遍历物品和背包容量,根据动态规划的状态转移方程逐步计算出最优解。

    最终返回dp[n][max_weight]即可得到背包能容纳的最大价值。

    3. 爬楼梯(Climbing Stairs):

    • 题目描述:假设你正在爬楼梯,每次只能爬一步或两步。请问,爬到第n阶楼梯有多少种不同的方法?
    • 示例代码:
       def climbStairs(n):
           if n <= 2:
               return n
           dp = [0] * (n + 1)
           dp[1] = 1
           dp[2] = 2
           for i in range(3, n + 1):
               dp[i] = dp[i - 1] + dp[i - 2]
           return dp[n]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    爬楼梯问题是动态规划中的经典问题,假设有n阶楼梯,每次可以爬1阶或2阶,问有多少种不同的方法可以爬到n阶。

    这个问题可以用递归和动态规划两种方法来解决,下面先介绍递归方法:

    递归方法:我们可以先假设有f(n)种方法爬到n阶,那么爬到n-1阶就有f(n-1)种方法,爬到n-2阶就有f(n-2)种方法,而爬到n阶只有两种情况,一种是从n-1阶爬一阶上来,另一种是从n-2阶爬两阶上来,所以f(n)=f(n-1)+f(n-2),这就是递归关系式。

    def climbStairs(n):
        if n <= 2:
            return n
        
        return climbStairs(n - 1) + climbStairs(n - 2)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    动态规划方法:动态规划方法和递归方法的思想是一样的,只是实现方式不同。在动态规划方法中,我们从底层开始逐步求解,而不是像递归方法那样从顶层开始求解。

    首先定义一个数组dp,其中dp[i]表示爬到第i阶有多少种方法。根据递归关系式,我们可以得到状态转移方程为dp[i]=dp[i-1]+dp[i-2],即爬到第i阶的方法数等于爬到第i-1阶的方法数加上爬到第i-2阶的方法数。

    接下来我们从底层开始逐步求解,先求出dp[1]和dp[2],然后根据状态转移方程逐步求出dp[3]、dp[4]、dp[5]…直到dp[n]。最后dp[n]就是我们要求的结果,即爬到第n阶有多少种方法。

    无论是递归方法还是动态规划方法,时间复杂度都是O(n),空间复杂度都是O(n),因此两种方法的时间和空间复杂度都是相同的。


    4. 最长公共子序列(Longest Common Subsequence):

    • 题目描述:给定两个字符串text1和text2,找到它们的最长公共子序列的长度。
    • 示例代码:
       def longestCommonSubsequence(text1, text2):
           m, n = len(text1), len(text2)
           dp = [[0] * (n + 1) for _ in range(m + 1)]
           for i in range(1, m + 1):
               for j in range(1, n + 1):
                   if text1[i - 1] == text2[j - 1]:
                       dp[i][j] = dp[i - 1][j - 1] + 1
                   else:
                       dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
           return dp[m][n]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5. 买卖股票的最佳时机(Best Time to Buy and Sell Stock):

    • 题目描述:给定一个数组,它的第i个元素是一支给定股票第i天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成两笔交易。
    • 示例代码:
       def maxProfit(prices):
           buy1, buy2 = float('-inf'), float('-inf')
           sell1, sell2 = 0, 0
           for price in prices:
               sell2 = max(sell2, buy2 + price)
               buy2 = max(buy2, sell1 - price)
               sell1 = max(sell1, buy1 + price)
               buy1 = max(buy1, -price)
           return sell2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6. 编辑距离(Edit Distance):

    • 题目描述:给定两个单词word1和word2,计算出将word1转换成word2所使用的最少操作次数。可以对一个单词进行插入、删除或替换操作。
    • 示例代码:
       def minDistance(word1, word2):
           m, n = len(word1), len(word2)
           dp = [[0] * (n + 1) for _ in range(m + 1)]
           for i in range(m + 1):
               dp[i][0] = i
           for j in range(n + 1):
               dp[0][j] = j
           for i in range(1, m + 1):
               for j in range(1, n + 1):
                   if word1[i - 1] == word2[j - 1]:
                       dp[i][j] = dp[i - 1][j - 1]
                   else:
                       dp[i][j] = min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j - 1]) + 1
           return dp[m][n]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    计算机毕业设计微信小程序汽车租赁平台
    OSPF —— OSPF协议分析(OSPF报文详解)
    7.canvas图形颜色设置
    mysql explain type 枚举
    Apache Knox安装测试
    DoozyUI⭐️二十一、Input:按键输入监听组件
    Adobe将类ChatGPT集成到PDF中
    STM32 ADC基础知识讲解
    【擎标】CCID信息系统服务商交付能力等级认证标准
    kotlin 消除强制非空!!
  • 原文地址:https://blog.csdn.net/qq_44810930/article/details/132884230