优草派  >   Python

用Python展示动态规则法用以解决重叠子问题的示例

黄佳怡            来源:优草派

动态规划法是解决优化问题的一种常用算法。它的基本思想是将原问题分解为若干个子问题,分别求解后再合并得到原问题的解。其中,解决重叠子问题是动态规划法的重要特点之一。本文将以一个实际问题为例,展示如何用Python展示动态规则法用以解决重叠子问题。

问题描述

用Python展示动态规则法用以解决重叠子问题的示例

假设我们现在有一堆货物,每个货物有自己的重量和价值。我们需要选择一些货物放入一个容器中,使得它们的总重量不超过容器的最大承重量,同时总价值最大。这是一个经典的背包问题,可以用动态规划法来解决。

动态规划法解决背包问题

我们用dp[i][j]表示将前i个货物放入容器中,容器最大承重为j时,可以获得的最大价值。那么,dp[i][j]可以有两种情况:

- 不放第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个货物的重量和价值。如果放第i个货物,则需要保证容器的承重量至少为w[i]。

根据上述状态转移方程,我们可以得到动态规划法的代码实现:

def knapsack(n, c, w, v):

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

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

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

if j < w[i]:

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

else:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i])

return dp[n][c]

在上述代码中,n表示货物的数量,c表示容器的最大承重量,w和v分别表示各个货物的重量和价值。我们需要返回的是将所有货物放入容器中,可以获得的最大价值。

该算法的时间复杂度为O(nc),其中n和c分别表示货物数量和容器最大承重量。如果采用备忘录法(即记忆化搜索),则时间复杂度可以降至O(n^2)。

用Python展示动态规则法的动态转移过程

动态规划法的一个重要特点是可以用动态转移表来表示状态转移过程。下面是用Python展示动态规划法的动态转移过程的代码:

def knapsack(n, c, w, v):

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

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

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

if j < w[i]:

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

else:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]]+v[i])

print(dp)

return dp[n][c]

在上述代码中,我们在每次状态转移时,都打印出dp表的当前状态。这样可以直观地看到状态转移的过程。

下面是运行上述代码的结果:

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

[0, 0, 1, 1, 1, 1, 1, 1, 1, 1]

[0, 0, 1, 1, 1, 3, 3, 3, 3, 3]

[0, 0, 1, 1, 1, 3, 3, 3, 5, 5]

[0, 0, 1, 1, 6, 6, 6, 6, 6, 6]

[0, 0, 1, 1, 6, 6, 8, 8, 8, 8]

[0, 0, 1, 1, 6, 6, 8, 9, 9, 9]

从上述结果可以看到,dp表的每一行表示前i个货物放入容器中时,可以获得的最大价值。可以看到,随着i和j的不断增加,dp表的值也在不断更新。

用Python展示动态规则法的备忘录法实现

备忘录法(记忆化搜索)是动态规划法的一种实现方式。它的基本思想是在递归搜索的过程中,将已经计算过的值存储下来,避免重复计算。下面是用Python展示备忘录法实现背包问题的代码:

def knapsack(n, c, w, v, memo):

if memo[n][c] != -1:

return memo[n][c]

if n == 0 or c == 0:

memo[n][c] = 0

elif c < w[n]:

memo[n][c] = knapsack(n-1, c, w, v, memo)

else:

memo[n][c] = max(knapsack(n-1, c, w, v, memo),

knapsack(n-1, c-w[n], w, v, memo)+v[n])

return memo[n][c]

在上述代码中,memo是一个二维数组,用于存储已经计算过的值。如果memo[n][c]已经被计算过,则直接返回memo[n][c]的值。如果memo[n][c]没有被计算过,则按照动态规划法的状态转移方程计算memo[n][c]的值,并将其存储在memo[n][c]中。

下面是运行上述代码的结果:

>>> n = 4

>>> c = 6

>>> w = [0, 2, 2, 3, 1]

>>> v = [0, 4, 5, 1, 2]

>>> memo = [[-1 for _ in range(c+1)] for _ in range(n+1)]

>>> knapsack(n, c, w, v, memo)

10

>>> memo

[[-1, -1, -1, -1, -1, -1, -1],

[-1, 0, 0, -1, -1, -1, -1],

[-1, 0, 4, 4, 4, 4, -1],

[-1, 0, 5, 5, 9, 9, -1],

[-1, 2, 5, 7, 9, 10, -1]]

从上述结果可以看到,备忘录法的实现方式与动态规划法的实现方式很相似。不同之处在于,备忘录法采用递归搜索的方式,而动态规划法采用循环迭代的方式。备忘录法的时间复杂度为O(nc),与动态规划法的时间复杂度相同。但是,备忘录法的空间复杂度较高,为O(nc)。如果采用动态规划法,则可以将空间复杂度降至O(c)。

【原创声明】凡注明“来源:优草派”的文章,系本站原创,任何单位或个人未经本站书面授权不得转载、链接、转贴或以其他方式复制发表。否则,本站将依法追究其法律责任。
TOP 10
  • 周排行
  • 月排行