动态规划是一种常见的算法设计思想,通过拆解问题,利用子问题的解来求解原问题。以下是一些使用动态规划的经典问题及其相应的解决方案。
1. 斐波那契数列
首先,我们介绍了计算斐波那契数列的两种方式:递归和非递归。递归实现简洁但效率较低,而非递归方式采用动态规划,通过递推式和重复子问题的思想,避免了重复计算,提高了效率。
# 斐波那契数列
def fibonacci(n):
if n == 1 or n == 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
# 斐波那契数列 非递归
def fibonacci_no_rec(n):
f = [0, 1, 1]
if n > 2:
for i in range(n - 2):
num = f[-1] + f[-2]
f.append(num)
return f[n]
2. 钢条切割问题
接着,我们解决了钢条切割问题,其中包括自顶向下和自底向上两种实现方式。这个问题通过动态规划的方式,通过构建递推关系,避免了重复计算,提高了求解效率。
# 钢条切割问题
# 自顶向下实现
p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 24, 30]
def cut_rod_rec_1(p, n):
if n == 0:
return 0
else:
res = p[n]
for i in range(1, n):
res = max(res, cut_rod_rec_1(p, i) + cut_rod_rec_1(p, n - i))
return res
print(cut_rod_rec_1(p, 9))
# 自底向上
def cut_rod_dp(p, n):
r = [0]
for i in range(1, n + 1):
res = 0
for j in range(1, i + 1):
res = max(res, p[j] + r[i - j])
r.append(res)
return r[n]
print(cut_rod_dp(p, 9))
3. 最长公共子序列
最后,我们介绍了最长公共子序列问题,通过构建二维表格来记录子问题的解,进而求解整个问题。该问题展示了动态规划在字符串匹配等领域的广泛应用。
# 最长公共子序列
def lcs_length(x, y):
m = len(x)
n = len(y)
c = [[0 for _ in range(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]:
c[i][j] = c[i - 1][j - 1] + 1
else:
c[i][j] = max(c[i - 1][j], c[i][j - 1])
for _ in c:
print(_)
return c[m][n]
print(lcs_length("ABCBDAB", "BDCABA"))