一些动态规划题解

1,303 阅读3分钟

小偷盗窃问题

  • 问题描述:

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例 1:

输入: [1,2,3,1] 输出: 4 解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入: [2,7,9,3,1] 输出: 12 解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 偷窃到的最高金额 = 2 + 9 + 1 = 12 。

  • 问题求解 题意简单说来就是不能偷相邻的两个房屋, 而且要尽量偷得多. 这是典型的动态规划问题, 思路如下, 由于后面房间能偷到的最大钱数取决于前面房间能偷到的最大钱数, 所以可以从第一间房子向后看. 建立一个数组dp, 数组中的第n个元素保存前n间房屋总共能偷到的最大钱数:
  1. 若只有一间房子, 则只能偷这间, 则前1间房屋能偷到的最大金额一已知, 保存下来;
  2. 若有两间, 则偷其中最多的, 则前两间房屋能偷到的最大金额已知, 保存下来;
  3. 若有3间, 则分为两种情况, 1)偷房屋 1和3; 2) 只偷房屋2. 比较这两种哪种获益大. 这可以抽象成两个子问题, 决定这两个子问题的关键是第三间房子偷与否. 分别把偷(子问题1)和不偷(子问题2) 的结果计算出来, 选出最大就是了. 现在, 前三间能偷到的最大金额已知, 保存下来, 以供偷第4、5...N 间房子时参考.
  4. 若有4间, 则又是两种情况: 1)偷4不偷3; 2)不偷4. 这两种情况只和前三间房屋偷到的金额(dp[3])和前两间房屋偷到的金额(dp[2])的结果有关,把这两种情况下的金额计算出来, 选择最大的就可以了, 即 max(房屋4的钱+dp[2], dp[3]);
  5. 由此可得, 第n间房屋偷还是不偷只要考虑 dp 数组中的dp[n-1]和dp[n-2]+当前可得的金额这两个因素就可以了.

所以状态方程为:max(dp[n-2] + thisValue, dp[n-1]), 代码为:

var rob = function(nums) {
    // 判断异常
    if(!nums || nums.length === 0){ 
        return 0;
    }
    // 边界条件
    if(nums.length < 3){
        return Math.max(...nums);
    }
    
    // 状态方程 dp(i) = max( dp(i-1) , dp(2) + arr[i])
    let dp = [];
    dp[0] = nums[0];
    dp[1] = Math.max(nums[0],nums[1]);
    
    for(let i = 2; i < nums.length; i++){
        dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);
    }
    
    return dp[dp.length-1];
};

待续...