动态规划算法的核心思想和经典题目

动态规划算法的核心思想和经典题目

动态规划(Dynamic Programming,简称 DP)是一种用于解决复杂问题的算法思想,通过将问题分解为更小的子问题,并存储这些子问题的结果以避免重复计算,从而提高算法效率。动态规划的核心思想是利用子问题的最优解来构建原问题的最优解

核心思想

  1. 分解问题:
    • 将原问题分解为更小的子问题。
  2. 存储子问题的结果:
    • 使用数组或其他数据结构存储子问题的结果,以避免重复计算。
  3. 构建最优解:
    • 利用子问题的最优解来构建原问题的最优解。

经典题目

以下是一些经典的动态规划题目及其解答:

1. 斐波那契数列

问题描述

计算斐波那契数列的第 n 个数。斐波那契数列定义如下:

F(0) = 0
F(1) = 1
F(n) = F(n-1) + F(n-2),其中 n >= 2

解答

function fibonacci(n) {
  if (n <= 1) return n;
  const dp = [0, 1];
  for (let i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}

// 示例
console.log(fibonacci(10)); // 输出: 55

2. 最长公共子序列(LCS)

问题描述

给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

解答

function longestCommonSubsequence(text1, text2) {
  const m = text1.length;
  const n = text2.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 (text1[i - 1] === text2[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];
}

// 示例
console.log(longestCommonSubsequence('abcde', 'ace')); // 输出: 3

3. 背包问题

问题描述

给定一个容量为 W 的背包和 N 个物品,每个物品有重量 weights[i] 和价值 values[i]。求在不超过背包容量的情况下,能够获得的最大价值。

解答

function knapsack(W, weights, values) {
  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 = 1; 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 = [2, 3, 4, 5];
const values = [3, 4, 5, 6];
const W = 5;
console.log(knapsack(W, weights, values)); // 输出: 7

4. 最长递增子序列(LIS)

问题描述

给定一个整数数组 nums,找到其中最长严格递增子序列的长度。

解答

function lengthOfLIS(nums) {
  if (nums.length === 0) return 0;
  const dp = Array(nums.length).fill(1);

  for (let i = 1; i < nums.length; i++) {
    for (let j = 0; j < i; j++) {
      if (nums[i] > nums[j]) {
        dp[i] = Math.max(dp[i], dp[j] + 1);
      }
    }
  }

  return Math.max(...dp);
}

// 示例
console.log(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18])); // 输出: 4

5. 编辑距离(Edit Distance)

问题描述

给定两个单词 word1 和 word2,计算将 word1 转换为 word2 所使用的最少操作数。你可以对一个单词进行以下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符

解答

function minDistance(word1, word2) {
  const m = word1.length;
  const n = word2.length;
  const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));

  for (let i = 0; i <= m; i++) {
    dp[i][0] = i;
  }
  for (let j = 0; j <= n; j++) {
    dp[0][j] = j;
  }

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

  return dp[m][n];
}

// 示例
console.log(minDistance('horse', 'ros')); // 输出: 3

总结

动态规划是一种强大的算法思想,通过将问题分解为更小的子问题,并存储这些子问题的结果以避免重复计算,从而提高算法效率。经典的动态规划题目包括斐波那契数列、最长公共子序列、背包问题、最长递增子序列和编辑距离等。通过理解和练习这些经典题目,你可以掌握动态规划的核心思想,并应用于解决复杂的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值