数据结构与算法之 leetcode 动态规划入门题 背包问题 / 最长子序列 解析

数据结构与算法阿木 发布于 4 天前 3 次阅读


动态规划入门题解析:背包问题与最长子序列

动态规划(Dynamic Programming,简称DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中广泛应用的方法。它通过将复杂问题分解为更小的子问题,并存储这些子问题的解,从而避免重复计算,提高算法效率。本文将围绕动态规划入门题中的背包问题与最长子序列问题进行解析,帮助读者理解动态规划的基本原理和应用。

背包问题

背包问题是一个经典的动态规划问题,主要分为两种类型:01背包问题和完全背包问题。

01背包问题

01背包问题是指给定一个背包容量和若干物品,每个物品有一个重量和一个价值,问如何选择物品使得背包内物品的总价值最大,同时不超过背包的容量。

解题思路

1. 定义状态:dp[i][j]表示前i个物品放入容量为j的背包中能获得的最大价值。

2. 状态转移方程:

- 如果不放入第i个物品,则dp[i][j] = dp[i-1][j]。

- 如果放入第i个物品,则dp[i][j] = dp[i-1][j-w[i]] + v[i],其中w[i]和v[i]分别表示第i个物品的重量和价值。

3. 初始化:dp[0][j] = 0,表示不放入任何物品时,背包价值为0。

4. 计算dp数组:按照状态转移方程计算dp数组。

5. 回溯求解:根据dp数组回溯求解最优解。

代码实现

python

def knapsack01(weights, values, capacity):


n = len(weights)


dp = [[0] (capacity + 1) for _ in range(n + 1)]

for i in range(1, n + 1):


for j in range(1, capacity + 1):


if j >= weights[i - 1]:


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]

weights = [2, 3, 4, 5]


values = [3, 4, 5, 6]


capacity = 5


print(knapsack01(weights, values, capacity))


完全背包问题

完全背包问题与01背包问题类似,但每个物品可以无限制地选择。

解题思路

1. 定义状态:dp[i][j]表示前i个物品放入容量为j的背包中能获得的最大价值。

2. 状态转移方程:

- dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]),其中w[i]和v[i]分别表示第i个物品的重量和价值。

3. 初始化:dp[0][j] = 0,表示不放入任何物品时,背包价值为0。

4. 计算dp数组:按照状态转移方程计算dp数组。

5. 回溯求解:根据dp数组回溯求解最优解。

代码实现

python

def knapsackFull(weights, values, capacity):


n = len(weights)


dp = [[0] (capacity + 1) for _ in range(n + 1)]

for i in range(1, n + 1):


for j in range(1, capacity + 1):


if j >= weights[i - 1]:


dp[i][j] = max(dp[i - 1][j], dp[i][j - weights[i - 1]] + values[i - 1])


else:


dp[i][j] = dp[i - 1][j]

return dp[n][capacity]

weights = [2, 3, 4, 5]


values = [3, 4, 5, 6]


capacity = 5


print(knapsackFull(weights, values, capacity))


最长子序列

最长子序列问题是指给定一个序列,找出该序列的最长子序列,使得子序列中的元素在原序列中按照相同的顺序出现。

解题思路

1. 定义状态:dp[i][j]表示以原序列的第i个元素结尾的最长子序列的长度。

2. 状态转移方程:

- 如果原序列的第i个元素与子序列的第j个元素相同,则dp[i][j] = dp[i - 1][j - 1] + 1。

- 否则,dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])。

3. 初始化:dp[0][j] = 0,表示空子序列的长度为0。

4. 计算dp数组:按照状态转移方程计算dp数组。

5. 回溯求解:根据dp数组回溯求解最长子序列。

代码实现

python

def longestSubsequence(nums):


n = len(nums)


dp = [[0] (n + 1) for _ in range(n + 1)]

for i in range(1, n + 1):


for j in range(1, n + 1):


if nums[i - 1] == nums[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[n][n]

nums = [1, 2, 3, 4, 5]


print(longestSubsequence(nums))


总结

本文通过解析背包问题与最长子序列问题,介绍了动态规划的基本原理和应用。动态规划是一种高效解决复杂问题的方法,通过将问题分解为更小的子问题,并存储这些子问题的解,避免了重复计算,提高了算法效率。在实际应用中,动态规划可以解决许多实际问题,如背包问题、最长公共子序列、最长递增子序列等。希望本文能帮助读者更好地理解动态规划,并在实际项目中运用它。