一些在前端开发中常见的算法-动态规划算法

动态规划(Dynamic Programming, DP)是一种优化算法设计技术,用于解决具有重叠子问题和最优子结构性质的问题。动态规划通过将问题拆解为子问题,并将子问题的结果保存起来,以避免重复计算,从而提高算法效率。

动态规划的基本思想

  1. 重叠子问题:将问题分解为重复的子问题,这些子问题在不同的地方被多次求解。
  2. 最优子结构:问题的最优解可以通过其子问题的最优解来构造。

动态规划的步骤

  1. 定义状态:确定问题的状态和状态之间的转移关系。
  2. 状态转移方程:写出状态转移方程,描述如何从一个状态转换到另一个状态。
  3. 初始化:定义初始状态及其对应的值。
  4. 计算状态:根据状态转移方程计算所有状态的值。
  5. 返回结果:从计算的状态中得到最终结果。

经典动态规划问题

以下是几个经典的动态规划问题及其逐步讲解,包括背包问题、最长公共子序列(LCS)和最小路径和。

1. 背包问题(0/1 Knapsack Problem)

问题描述:给定一个背包的容量和若干个物品,每个物品有一个重量和一个价值。问如何选择物品使得在不超过背包容量的情况下,物品的总价值最大。

步骤:
  1. 定义状态

    • dp[i][w] 表示前 i 个物品中,在容量为 w 的背包中可以获得的最大价值。
  2. 状态转移方程

    • 如果不选择第 i 个物品:dp[i][w] = dp[i-1][w]
    • 如果选择第 i 个物品:dp[i][w] = dp[i-1][w - weight[i]] + value[i]
    • 综合考虑:dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight[i]] + value[i])
  3. 初始化

    • dp[0][w] = 0,表示没有物品时,最大价值为 0。
  4. 计算状态

    • 根据状态转移方程逐步计算所有状态的值。
  5. 返回结果

    • dp[n][W] 为所求的最大价值,其中 n 是物品总数,W 是背包容量。
代码实现:
function knapsack(weights, values, W) {
  const n = weights.length;
  const dp = Array.from({ length: n + 1 }, () => Array(W + 1).fill(0));

  for (let i = 1; i <= n; i++) {
    for (let w = 0; w <= W; w++) {
      if (weights[i - 1] <= w) {
        dp[i][w] = Math.max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]);
      } else {
        dp[i][w] = dp[i - 1][w];
      }
    }
  }

  return dp[n][W];
}

// 示例
const weights = [1, 2, 3];
const values = [60, 100, 120];
const W = 5;
console.log(knapsack(weights, values, W)); // 输出: 220
2. 最长公共子序列(Longest Common Subsequence, LCS)

问题描述:给定两个字符串,找出它们的最长公共子序列(LCS)的长度。子序列是指删除一些字符(可以不删除任何字符),不改变其余字符的顺序,得到的子序列。

步骤:
  1. 定义状态

    • dp[i][j] 表示字符串 A 的前 i 个字符与字符串 B 的前 j 个字符的最长公共子序列的长度。
  2. 状态转移方程

    • 如果 A[i-1] == B[j-1]dp[i][j] = dp[i-1][j-1] + 1
    • 如果 A[i-1] != B[j-1]dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  3. 初始化

    • dp[0][j] = 0dp[i][0] = 0,表示一个字符串为空时的 LCS 长度为 0。
  4. 计算状态

    • 根据状态转移方程逐步计算所有状态的值。
  5. 返回结果

    • dp[m][n] 为所求的 LCS 长度,其中 mn 是两个字符串的长度。
代码实现:
function longestCommonSubsequence(A, B) {
  const m = A.length;
  const n = B.length;
  const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));

  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (A[i - 1] === B[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
      } else {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
      }
    }
  }

  return dp[m][n];
}

// 示例
const A = "ABCBDAB";
const B = "BDCAB";
console.log(longestCommonSubsequence(A, B)); // 输出: 4
3. 最小路径和(Minimum Path Sum)

问题描述:给定一个 m x n 的网格,每个格子中有一个非负整数。起始点在左上角,终点在右下角,只能向右或向下移动。求从起始点到终点的最小路径和。

步骤:
  1. 定义状态

    • dp[i][j] 表示从 (0, 0)(i, j) 的最小路径和。
  2. 状态转移方程

    • dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])
    • 需要处理边界条件:
      • dp[0][j] = dp[0][j-1] + grid[0][j]
      • dp[i][0] = dp[i-1][0] + grid[i][0]
  3. 初始化

    • dp[0][0] = grid[0][0],表示起始点的路径和为其自身的值。
  4. 计算状态

    • 根据状态转移方程逐步计算所有状态的值。
  5. 返回结果

    • dp[m-1][n-1] 为从起始点到终点的最小路径和。
代码实现:
function minPathSum(grid) {
  const m = grid.length;
  const n = grid[0].length;
  const dp = Array.from({ length: m }, () => Array(n).fill(0));

  dp[0][0] = grid[0][0];

  // 初始化第一行
  for (let j = 1; j < n; j++) {
    dp[0][j] = dp[0][j - 1] + grid[0][j];
  }

  // 初始化第一列
  for (let i = 1; i < m; i++) {
    dp[i][0] = dp[i - 1][0] + grid[i][0];
  }

  // 填充 dp 表
  for (let i = 1; i < m; i++) {
    for (let j = 1; j < n; j++) {
      dp[i][j] = grid[i][j] + Math.min(dp[i - 1][j], dp[i][j - 1]);
    }
  }

  return dp[m - 1][n - 1];
}

// 示例
const grid = [
  [1, 3, 1],
  [1, 5, 1],
  [4, 2, 1]
];
console.log(minPathSum(grid)); // 输出: 7

总结

  • 动态规划 适用于具有重叠子问题和最优子结构性质的问题。
  • 定义状态:明确每个子问题的状态。
  • 状态转移方程:描述如何从子问题的解构造出当前问题的解。
  • 初始化:设定边界条件。
  • 计算状态:按照状态转移方程计算状态值。
  • 返回结果:从计算得到的状态中获取最终结果。通过动态规划,你可以有效地解决许多复杂的优化问题,减少计算时间并提高效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

跳房子的前端

你的打赏能让我更有力地创造

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

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

打赏作者

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

抵扣说明:

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

余额充值