动态规划问题

动态规划(Dynamic Programming,简称DP)

是一种常用的解决优化问题的算法思想,它通常用于求解具有重叠子问题和最优子结构性质的问题。

动态规划算法常用于解决具有最优子结构性质的问题,例如最长递增子序列、背包问题、最短路径问题等。

下面是关于动态规划的一些重要知识点:

1. 重叠子问题(Overlapping Subproblems)

动态规划算法的核心思想之一是重叠子问题。在解决一个问题的过程中,如果问题的求解过程中涉及到了重复计算相同的子问题,则称这个问题具有重叠子问题的性质。动态规划算法通过将子问题的解存储起来,避免了重复计算,从而提高了算法的效率。

2. 最优子结构(Optimal Substructure)

动态规划算法的另一个核心思想是最优子结构。一个问题具有最优子结构的性质,意味着问题的最优解可以通过其子问题的最优解来构造。换句话说,如果一个问题的最优解可以由其子问题的最优解推导出来,则称该问题具有最优子结构性质。

3. 状态(State)

在动态规划算法中,状态是问题的解空间中的一个点,通常用一个或多个变量来表示。状态可以是问题的某个属性的取值,也可以是问题的子结构。动态规划算法通过定义状态来描述问题的性质,并根据状态之间的转移关系来构建状态转移方程。

4. 状态转移方程(State Transition Equation)

状态转移方程描述了问题中状态之间的转移关系,即从一个状态转移到另一个状态的过程。动态规划算法通过定义状态转移方程来描述问题的递推关系,并根据状态转移方程来计算问题的最优解。

5. 初始状态(Base Case)

初始状态是动态规划算法中的起始点,通常是问题的最小规模的解。初始状态的定义是问题求解的基础,是动态规划算法的一个重要组成部分。

6. 计算顺序(Computational Order)

计算顺序指的是动态规划算法中计算状态之间转移关系的顺序。通常情况下,计算顺序是按照状态之间的依赖关系从小到大进行计算的,确保每个状态的计算依赖于之前的状态。

7. 解的求解(Solution Reconstruction)

解的求解是动态规划算法的最后一步,通常是根据计算得到的状态和状态转移方程来构造问题的最优解。解的求解过程是动态规划算法的关键,通常需要根据问题的具体性质来确定解的构造方法。

动态规划算法通常分为以下几个步骤:

  1. 定义状态:首先明确定义问题的状态,找出问题中变化的部分,并将其转化为状态变量。

  2. 状态转移方程:根据问题的要求,确定状态之间的转移关系。这个转移关系通常通过状态之间的递推关系来描述,即找出当前状态与之前状态之间的关系。

  3. 初始状态:确定初始状态,即问题的最小规模的解,通常是状态转移方程中的边界条件。

  4. 计算顺序:确定状态之间的计算顺序,通常是按照状态转移方程中的依赖关系从小到大进行计算。

  5. 解的求解:根据问题的定义,得到最终的解。

示例

下面是一个简单的例子,演示了如何使用动态规划算法

题目:

假设有一个背包,它能容纳的重量为 W。现在有 n 个物品,每个物品的重量分别为 weights[i],价值为 values[i]。我们希望从这些物品中挑选一些放入背包中,使得放入的物品总重量不超过背包容量,并且总价值最大。这是一个经典的背包问题,可以使用动态规划来解决。

1. 定义状态

我们将问题的状态定义为背包的剩余容量和可选物品的索引。即状态可以用 (remainingCapacity, currentIndex) 表示,其中 remainingCapacity 表示背包的剩余容量,currentIndex 表示当前可选的物品索引。

2. 状态转移方程

我们可以定义状态转移方程来描述问题的递推关系。设 dp[i][j] 表示考虑前 i 个物品,在背包容量为 j 时,能够获得的最大总价值。则状态转移方程为:

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

 其中,dp[i-1][j] 表示不选择第 i 个物品时的最大总价值,dp[i-1][j-weights[i]] + values[i] 表示选择第 i 个物品时的最大总价值。

3. 初始状态

初始状态为 dp[0][j] = 0(表示没有物品可选时的最大总价值),dp[i][0] = 0(表示背包容量为 0 时的最大总价值为 0)。

4. 计算顺序

我们可以按照状态转移方程中的依赖关系从小到大进行计算。即先计算出 dp[0][j] 和 dp[i][0],然后依次计算出 dp[1][j]、dp[2][j],直到计算出 dp[n][W]。

5. 解的求解

根据计算得到的 dp 数组,我们可以从中找出最终的解。在本问题中,我们需要找出 dp[n][W],即考虑前 n 个物品,背包容量为 W 时的最大总价值。

/**
 * 01背包问题
 *
 * @param weights 物品重量数组
 * @param values 物品价值数组
 * @param W 背包容量
 * @returns 返回最大价值
 */
function knapsack(weights, values, W) {
  const n = weights.length;

  // 创建一个二维数组dp,用于存储中间结果
  const dp = Array.from({ length: n + 1 }, () => Array(W + 1).fill(0));

  // 遍历每个物品
  for (let i = 1; i <= n; i++) {
    // 遍历每个背包容量
    for (let j = 1; j <= W; j++) {
      // 如果当前物品的重量小于等于当前背包容量
      if (weights[i - 1] <= j) {
        // 更新dp[i][j]的值为当前背包容量下,选择当前物品和不选择当前物品中的较大值
        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);
      } else {
        // 如果当前物品的重量大于当前背包容量,则无法选择当前物品,dp[i][j]的值与dp[i-1][j]相同
        dp[i][j] = dp[i - 1][j];
      }
    }
  }

  // 返回dp[n][W],即背包容量为W时,能够装载的物品的最大价值
  return dp[n][W];
}

const weights = [2, 3, 4, 5];
const values = [3, 4, 5, 6];
const W = 8;
console.log('背包能获得的最大总价值为:', knapsack(weights, values, W));

代码题目:(不断添加)

求解斐波那契数列

/**
 * 计算斐波那契数列的第n项
 *
 * @param n 第n项,n为大于等于0的整数
 * @returns 返回斐波那契数列的第n项
 */
function fibonacci(n) {
  // 如果 n 小于等于 1,则直接返回 n
  if (n <= 1) {
      return n;
  }
  // 创建一个长度为 n+1 的数组 dp,用于存储斐波那契数列的值
  let dp = new Array(n + 1);
  // 初始化 dp 数组的前两个元素
  dp[0] = 0;
  dp[1] = 1;
  // 从第三个元素开始,计算斐波那契数列的值
  for (let i = 2; i <= n; i++) {
      dp[i] = dp[i - 1] + dp[i - 2];
  }
  // 返回斐波那契数列的第 n 个值
  return dp[n];
}

console.log('斐波那契数列的第 10 项为:', fibonacci(10)); // 输出:55
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿online

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值