动态规划问题示例

爬楼梯

function climbStairs(n){
    if(n<=1){return 1};
    let dep = [];
    dep[0]=1;
    dep[1]=1;
    for(let i=2;i<=n;i++){
        dep[i] = dep[i-1]+dep[i-2]
    }
    return dep[n];
}
console.log(climbStairs(5))

买票股票

function maxProfit(prices) {
    if (prices.length === 0) {
      return 0;
    }
    let minPrice = prices[0];
    let maxProfit = 0;
  
    for (let i = 1; i < prices.length; i++) {
      minPrice = Math.min(minPrice, prices[i]);
      maxProfit = Math.max(maxProfit, prices[i] - minPrice);
    }
    return maxProfit;
  }
  
  // 示例用法
  const prices = [7, 1, 5, 3, 6, 4];
  console.log(maxProfit(prices)); // 输出: 5 (在第2天买入,第5天卖出)

最大连续子数和

function maxSubArray(nums) {
    let maxSoFar = nums[0];
    let maxEndingHere = nums[0];
    // maxSoFar 用于存储目前为止发现的最大子数组和
    // maxEndingHere 用于存储以当前元素结尾的最大子数组和
    let start = 0;
    let end=0;
    for (let i = 1; i < nums.length; i++) {
        let newMaxEndingHere = Math.max(nums[i], maxEndingHere + nums[i]);
        if(maxSoFar<newMaxEndingHere){
            maxSoFar = newMaxEndingHere;
            // maxSoFar 改变了才会改变需要截取的数组
            if(nums[i]>maxEndingHere + nums[i]){
                start = i;
                end = i;
            }else if(nums[i]<maxEndingHere + nums[i]){
                end = i;
            }
        }
        maxEndingHere = newMaxEndingHere
        maxSoFar = Math.max(maxSoFar, maxEndingHere);
    }
    let childArr = nums.slice(start,end+1)
    return {maxSoFar,childArr};
}
console.log(maxSubArray([-2,1,-3,4,-1,2,1,-5,4]))   //[4,-1,2,1]  6

0-1背包问题

对于每个物品 i 和每个容量 j,我们有两种选择:

  • 不放入物品 i:此时最大价值就是前 i-1 个物品放入容量为 j 的背包中的最大价值,即 dp[i-1][j]。
  • 放入物品 i:如果物品 i 的重量 w[i] 小于等于当前背包容量 j,我们可以选择放入物品 i。此时,背包的剩余容量变为 j - w[i],我们需要从前 i-1 个物品中选择一些放入剩余容量的背包中,以获得最大价值。这个最大价值是 dp[i-1][j-w[i]]。再加上物品 i 的价值 v[i],总价值就是 dp[i-1][j-w[i]] + v[i]。

综上所述,我们可以得到状态转移方程:

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

function knapsack(weights, values, maxWeight) {
    const n = weights.length;
    const dp = Array.from({ length: n + 1 }, () => Array(maxWeight + 1).fill(0));
    // 动态规划填表
    for (let i = 1; i <= n; i++) {
      for (let w = 1; w <= maxWeight; 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]);
          console.log(dp[i - 1][w],'----', dp[i - 1][w - weights[i - 1]] ,values[i - 1])
        } else {
          // 如果当前物品不能放入背包
          dp[i][w] = dp[i - 1][w];
        }
      }
    }
    // dp[n][maxWeight] 存储了最大价值
    return dp[n][maxWeight];
  }
  // 示例
  const weights = [2, 3, 4, 5];
  const values = [3, 4, 5, 6];
  const maxWeight = 5;
  
  console.log(knapsack(weights, values, maxWeight)); // 输出:7

最长公共子序列

动态规划解决最长公共子序列(Longest Common Subsequence, LCS)问题的步骤如下:

  1. 定义状态:定义一个二维数组 dp[i][j],其中 dp[i][j] 表示字符串 X 的前 i 个字符和字符串 Y 的前 j 个字符的最长公共子序列的长度。

  2. 初始化:初始化 dp 数组的第一行和第一列为 0,因为一个空字符串与任何字符串的最长公共子序列长度都是 0。

  3. 状态转移方程

  • 如果 X[i-1] == Y[j-1],则 dp[i][j] = dp[i-1][j-1] + 1,表示当前字符可以构成最长公共子序列的一部分。
  • 如果 X[i-1] != Y[j-1],则 dp[i][j] = max(dp[i-1][j], dp[i][j-1]),表示忽略当前字符,取上一个字符的最长公共子序列。
  1. 计算最优值dp 数组的最后一个元素 dp[m][n] 即为最长公共子序列的长度。

  2. 回溯找到子序列:从 dp[m][n] 开始,根据状态转移方程反向回溯,构建最长公共子序列。

  3. 输出结果:输出最长公共子序列的长度和字符序列。

function longestCommonSubsequence(str1, str2) {
    const m = str1.length;
    const n = str2.length;

    // 创建一个 (m+1) x (n+1) 的二维数组 dp
    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 (str1[i - 1] === str2[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]);
            }
        }
    }

    // dp[m][n] 存储了 str1 和 str2 之间的最长公共子序列的长度
    // return dp[m][n];

    let lcs = '';
    let i = m;
    let j = n;
    while (i > 0 && j > 0) {
        if (str1[i - 1] === str2[j - 1]) {
            lcs = str1[i - 1] + lcs;
            i--;
            j--;
        } else if (dp[i - 1][j] > dp[i][j - 1]) {
            i--;
        } else {
            j--;
        }
    }

    return lcs;
}

// 示例
console.log(longestCommonSubsequence("abcde", "ace")); // 输出:3
console.log(longestCommonSubsequence("ABCDEF", "ACDFE")); // 输出:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值