动态规划的基本概念
- 重叠子问题:在求解问题的过程中,会多次遇到相同的子问题。
- 最优子结构:问题的最优解包含其子问题的最优解。
动态规划的一般步骤
- 定义状态:将原问题划分为一系列子问题,并定义状态来表示这些子问题的解。
- 状态转移方程:根据子问题的解来推导出原问题的解,即定义状态之间的转移关系。
- 初始化:确定状态转移的起点或边界条件。
- 计算顺序:根据状态转移方程,确定计算状态的顺序(通常是自底向上)。
- 结果存储:存储中间结果,避免重复计算。
例子:最长公共子序列(LCS)
LCS问题是另一个典型的动态规划问题,它要求找到两个序列的最长公共子序列
动态规划解法
def lcs(X, Y):
m, n = len(X), len(Y)
L = [[0] * (n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
if X[i-1] == Y[j-1]:
L[i][j] = L[i-1][j-1] + 1
else:
L[i][j] = max(L[i-1][j], L[i][j-1])
# 构造LCS
index = L[m][n]
lcs_str = [""] * (index+1)
lcs_str[index] = ""
i, j = m, n
while i > 0 and j > 0:
if X[i-1] == Y[j-1]:
lcs_str[index-1] = X[i-1]
i -= 1
j -= 1
index -= 1
elif L[i-1][j] > L[i][j-1]:
i -= 1
else:
j -= 1
return "".join(lcs_str)
# 示例
X = "AGGTAB"
Y = "GXTXAYB"
print(lcs(X, Y)) # 输出: GTAB
不同类型问题的几个例子,这些问题可以用不同的算法或方法来解决:
1. 排序问题
问题描述:给定一个整数数组,你需要将它排序。
示例:
- 输入:[3, 1, 4, 1, 5, 9, 2, 6]
- 输出:[1, 1, 2, 3, 4, 5, 6, 9]
解决方法:可以使用多种排序算法,如快速排序、归并排序、堆排序、插入排序、冒泡排序等。
2. 搜索问题
问题描述:在一个未排序或已排序的数组中查找一个特定的元素。
示例:
- 输入:数组 [1, 3, 5, 7, 9],目标值 7
- 输出:索引 3(因为 7 在索引 3 的位置)
解决方法:如果数组已排序,可以使用二分查找。如果未排序,可以使用线性搜索。
3. 字符串匹配问题
问题描述:在给定的文本字符串中查找一个模式字符串的所有出现位置。
示例:
- 输入:文本 "hello world",模式 "world"
- 输出:索引 6(因为 "world" 从索引 6 开始)
解决方法:可以使用朴素字符串匹配算法、KMP(Knuth-Morris-Pratt)算法、Boyer-Moore 算法等。
4. 路径问题
问题描述:在图中找到从一个顶点到另一个顶点的最短路径。
示例:
- 输入:图的邻接矩阵,起点 s,终点 t
- 输出:从 s 到 t 的最短路径长度及路径
解决方法:可以使用 Dijkstra 算法(适用于带权无向图,且所有边的权重都非负)、Bellman-Ford 算法(可以处理负权边)、Floyd-Warshall 算法(计算图中所有顶点对之间的最短路径)。
5. 背包问题
问题描述:给定一组物品,每种物品都有自己的重量和价值,在限定的总重量内,如何选择物品使得总价值最大。
示例:
- 输入:物品重量 [2, 3, 4, 5],物品价值 [3, 4, 5, 6],背包容量 8
- 输出:最大价值 9(选择第二和第三件物品)
解决方法:可以使用动态规划解决 0-1 背包问题、完全背包问题等。
6. 旅行商问题(TSP)
问题描述:旅行商需要访问 n 个城市,每个城市恰好访问一次,最后回到出发城市,要求找到最短的路径。
示例:
- 输入:城市间的距离矩阵
- 输出:访问所有城市并返回出发点的最短路径
解决方法:这是一个 NP-hard 问题,没有已知的多项式时间解法。常用的近似算法包括贪心算法、遗传算法、模拟退火等。