动态规划 5 大经典问题解析:从最短路径到资源分配,附 Python 代码

发布时间:2026/7/12 4:21:13
动态规划 5 大经典问题解析:从最短路径到资源分配,附 Python 代码 动态规划五大经典问题实战解析从理论到Python实现动态规划Dynamic Programming作为算法设计中的核心思想在解决优化问题时展现出强大的威力。本文将深入探讨五大经典动态规划问题通过清晰的Python代码实现帮助读者掌握这一重要算法范式。1. 动态规划基础与核心思想动态规划是一种分阶段解决问题的数学方法由美国数学家理查德·贝尔曼在20世纪50年代提出。其核心在于将复杂问题分解为相互关联的子问题通过保存子问题的解避免重复计算显著提高算法效率。动态规划三大特征最优子结构问题的最优解包含子问题的最优解重叠子问题不同决策序列会重复访问相同的子问题无后效性未来决策只与当前状态有关与过去决策无关# 动态规划基本框架示例 def dp_template(): # 1. 定义状态 dp [0] * n # 2. 初始化基础情况 dp[0] base_case # 3. 状态转移 for i in range(1, n): dp[i] transition(dp[i-1], ...) return dp[-1]2. 最短路径问题最短路径问题是动态规划的经典应用场景Dijkstra算法和Floyd算法都是基于动态规划思想发展而来。问题描述给定带权有向图求从起点到终点的最短路径。状态转移方程dp[i][j] min(dp[i][k] dp[k][j], dp[i][j])def floyd(graph): n len(graph) dist [[float(inf)]*n for _ in range(n)] # 初始化 for i in range(n): for j in range(n): if i j: dist[i][j] 0 elif graph[i][j] ! 0: dist[i][j] graph[i][j] # 动态规划求解 for k in range(n): for i in range(n): for j in range(n): dist[i][j] min(dist[i][j], dist[i][k]dist[k][j]) return dist提示Floyd算法时间复杂度为O(n³)适合解决全源最短路径问题3. 背包问题背包问题展现了动态规划在资源分配中的强大能力分为0-1背包和完全背包两种基本形式。0-1背包状态转移方程dp[i][j] max(dp[i-1][j], dp[i-1][j-w[i]] v[i])def knapsack_01(weights, values, capacity): n len(weights) dp [[0]*(capacity1) for _ in range(n1)] for i in range(1, n1): for j in range(1, capacity1): if weights[i-1] j: dp[i][j] max(dp[i-1][j], dp[i-1][j-weights[i-1]] values[i-1]) else: dp[i][j] dp[i-1][j] return dp[n][capacity] # 空间优化版本 def knapsack_01_optimized(weights, values, capacity): dp [0]*(capacity1) for i in range(len(weights)): for j in range(capacity, weights[i]-1, -1): dp[j] max(dp[j], dp[j-weights[i]] values[i]) return dp[capacity]4. 最长公共子序列(LCS)LCS问题展示了动态规划在序列比对中的应用是生物信息学和文本处理的基础算法。状态转移方程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])def longest_common_subsequence(text1, text2): m, n len(text1), len(text2) dp [[0]*(n1) for _ in range(m1)] for i in range(1, m1): for j in range(1, n1): 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]5. 矩阵链乘法矩阵链乘法问题展示了动态规划在优化计算顺序方面的应用能显著减少矩阵相乘的计算量。状态转移方程dp[i][j] min(dp[i][k] dp[k1][j] p[i-1]*p[k]*p[j])def matrix_chain_order(p): n len(p) - 1 dp [[0]*n for _ in range(n)] for l in range(2, n1): # 链长度 for i in range(n - l 1): j i l - 1 dp[i][j] float(inf) for k in range(i, j): cost dp[i][k] dp[k1][j] p[i]*p[k1]*p[j1] if cost dp[i][j]: dp[i][j] cost return dp[0][n-1]6. 资源分配问题资源分配问题是动态规划在管理运筹学中的典型应用用于优化有限资源的分配策略。状态转移方程dp[i][j] max(dp[i-1][k] f(i, j-k)) for 0 ≤ k ≤ jdef resource_allocation(resources, projects, profit_func): dp [[0]*(resources1) for _ in range(projects1)] for i in range(1, projects1): for j in range(resources1): max_val 0 for k in range(j1): current dp[i-1][j-k] profit_func(i, k) if current max_val: max_val current dp[i][j] max_val return dp[projects][resources]动态规划问题特征对比表问题类型状态定义转移方程复杂度典型应用场景最短路径节点间距离O(n³)路由规划、导航系统背包问题物品与容量O(nW)资源分配、投资组合LCS序列位置O(mn)生物序列比对、版本控制矩阵链乘矩阵区间O(n³)编译器优化、数值计算资源分配项目与资源O(nm²)生产计划、预算分配7. 动态规划优化技巧在实际应用中我们常常需要对基本动态规划算法进行优化空间优化很多情况下可以只保留必要状态将空间复杂度从O(n²)降到O(n)# 斐波那契数列的空间优化 def fib(n): if n 2: return n a, b 0, 1 for _ in range(2, n1): a, b b, a b return b记忆化搜索采用递归缓存的方式实现动态规划from functools import lru_cache lru_cache(maxsizeNone) def fib_memo(n): if n 2: return n return fib_memo(n-1) fib_memo(n-2)状态压缩使用位运算等技巧压缩状态表示def tsp(graph): n len(graph) dp [[float(inf)]*n for _ in range(1n)] dp[1][0] 0 for mask in range(1n): for u in range(n): if not (mask (1u)): continue for v in range(n): if mask (1v): continue new_mask mask | (1v) dp[new_mask][v] min(dp[new_mask][v], dp[mask][u] graph[u][v]) return min(dp[(1n)-1][u] graph[u][0] for u in range(n))掌握动态规划需要大量练习从理解问题本质开始逐步构建状态定义和转移方程。建议从简单问题入手如爬楼梯、硬币兑换等再逐步挑战更复杂的问题。