当前位置:首页算法 > 正文

动态规划法代码实现思路

作者:野牛程序员:2023-05-15 18:59:30算法阅读 2685

动态规划是一种常用的算法思想,用于解决具有重叠子问题和最优子结构性质的问题。其核心思想是将原问题分解为若干个子问题,并保存子问题的解,以避免重复计算,从而提高效率。

以下是动态规划的代码实现思路:

  1. 确定问题的状态:将原问题抽象为一个或多个状态,这些状态描述了问题的某些特征或变量。状态应该能够唯一地确定问题的解。

  2. 定义状态转移方程:确定状态之间的关系,并用一个函数或表达式表示。这个方程通常由问题的定义和限制条件决定。

  3. 初始化:确定初始状态的值或边界条件。

  4. 递推计算:根据状态转移方程,使用递推的方式计算中间状态的值。通常,我们按照一定的顺序计算状态,确保在计算当前状态之前已经计算了其依赖的状态。

  5. 求解最优解:根据最终的状态或问题要求,确定最优解的值或具体解。

下面以一个简单的Python例子说明动态规划的代码实现思路,假设我们要计算斐波那契数列的第 n 个数:

def fibonacci(n):
    # 初始化状态
    fib = [0, 1]

    # 递推计算
    for i in range(2, n + 1):
        fib.append(fib[i - 1] + fib[i - 2])

    # 返回最优解
    return fib[n]

在上述代码中,我们通过动态规划的思想计算斐波那契数列的第 n 个数。其中,状态的定义是斐波那契数列的第 i 个数,状态转移方程是当前数等于前两个数之和,初始状态为前两个数 0 和 1。通过递推计算,我们得到最终的斐波那契数列第 n 个数的值。

以下是一个用C++演示动态规划的例子,解决背包问题(Knapsack Problem):

#include <iostream>
#include <vector>

using namespace std;

// 背包问题的动态规划解法
int knapsack(int W, vector<int>& weights, vector<int>& values) {
    int n = weights.size(); // 物品数量
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0)); // 创建二维数组dp,用于保存子问题的解

    // 递推计算子问题的解
    for (int i = 1; i <= n; ++i) { // 遍历每个物品
        for (int j = 1; j <= W; ++j) { // 遍历每个可能的背包容量
            if (weights[i - 1] <= j) {
                // 如果第i个物品的重量小于等于当前背包容量j
                // 有两种选择:放入第i个物品或不放入第i个物品
                dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]]);
                // 在放入和不放入之间选择价值最大的方案
                // 如果放入第i个物品,将其价值加上剩余容量j减去第i个物品重量时的最大价值
            } else {
                // 如果第i个物品的重量大于当前背包容量j
                // 无法放入第i个物品,价值与前一个物品保持一致
                dp[i][j] = dp[i - 1][j];
            }
        }
    }

    return dp[n][W]; // 返回给定背包容量下的最大价值
}

int main() {
    int W = 10; // 背包容量
    vector<int> weights = {2, 3, 4, 5}; // 物品的重量
    vector<int> values = {3, 4, 5, 6}; // 物品的价值

    int max_value = knapsack(W, weights, values); // 计算可以获得的最大价值

    cout << "最大价值: " << max_value << endl; // 输出最大价值

    return 0;
}


在上述代码中,我们使用动态规划算法解决了背包问题。我们定义了一个二维数组 dp 来保存子问题的解,其中 dp[i][j] 表示在前 i 个物品中,背包容量为 j 时可以获得的最大价值。通过两层循环,我们按顺序计算所有子问题的解,并将最优解保存在 dp[n][W] 中,其中 n 是物品的数量,W 是背包的容量。

这个例子中,我们演示了如何使用动态规划算法解决背包问题,但实际上动态规划可以应用于各种具有重叠子问题和最优子结构性质的问题。根据问题的特点,我们需要确定问题的状态、定义状态转移方程,并按照初始化和递推计算的方式实现代码。


在上面的程序中,我们通过动态规划算法解决了背包问题。现在来详细说明一下状态的定义、状态转移方程和初始状态的含义。

  1. 状态的定义: 在这个问题中,我们选择以背包中物品的数量和背包的容量作为状态。具体地,我们定义 dp[i][j] 表示在前 i 个物品中,背包容量为 j 时可以获得的最大价值。

  2. 状态转移方程: 状态转移方程描述了如何根据已知状态计算出新状态的值。对于背包问题,我们可以根据以下方式来更新状态:

    因此,状态转移方程可以表示为:

    • 选择放入第 i 个物品,那么背包中的总价值为 values[i - 1] 加上前 i - 1 个物品在剩余容量 j - weights[i - 1] 时的最大价值,即 values[i - 1] + dp[i - 1][j - weights[i - 1]]

    • 不选择放入第 i 个物品,背包中的总价值为前 i - 1 个物品在容量 j 时的最大价值,即 dp[i - 1][j]

    • 如果第 i 个物品的重量 weights[i - 1] 小于等于当前背包容量 j,我们有两种选择:

    • 我们需要在上述两种选择中选取较大的价值,将其赋值给 dp[i][j],表示在前 i 个物品中,背包容量为 j 时可以获得的最大价值。

dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]])
  1. 初始状态: 初始状态指的是边界情况或者基础情况下的状态值。对于背包问题,初始状态是背包容量为 0 时的最大价值,即 dp[i][0] = 0。另外,当没有物品可选时,即 i = 0 时,背包中的总价值也是 0,即 dp[0][j] = 0

通过上述的状态定义、状态转移方程和初始状态,我们可以使用动态规划算法解决背包问题,计算出在给定背包容量下能够获取的最大价值。


下面再详细讲解一下  dp[i][j] = max(dp[i - 1][j], values[i - 1] + dp[i - 1][j - weights[i - 1]]); 的意思:

首先,dp[i][j] 表示考虑前 i 个物品,在背包容量为 j 时所能获得的最大价值。

现在,我们来看两个选择:

  1. 不选取第 i 个物品:在这种情况下,背包中的物品保持不变,所以最大价值为 dp[i-1][j]

  2. 选择第 i 个物品:在这种情况下,我们将第 i 个物品放入背包中,并且需要考虑剩余的容量 j - weights[i-1]。此时,总的最大价值为 values[i-1](第 i 个物品的价值)加上考虑前 i-1 个物品、背包容量为 j - weights[i-1] 时的最大价值,即 dp[i-1][j - weights[i-1]]

由于我们的目标是获得最大价值,因此我们在这两种选择中选择价值更大的方案,这就是使用 max() 函数的作用。

因此,dp[i][j] 的更新规则是取这两个选择中的最大值,表示在考虑第 i 个物品、背包容量为 j 的情况下所能获得的最大价值。




野牛程序员教少儿编程与信息学奥赛-微信|电话:15892516892
野牛程序员教少儿编程与信息学竞赛-微信|电话:15892516892
相关推荐

最新推荐

热门点击