动态规划问题—打家劫舍

228 阅读2分钟

这是我参与更文挑战的第3天,活动详情查看:更文挑战

问题描述

小偷进行偷窃,不能偷窃相邻的两个房间,不然会触发报警,请问怎样才能窃取到最大的金额。

金额数字用数组表示

[1, 2, 3, 1]

动态规划的又一道基础题,先分析问题子结构,再列公式。

看一个问题能不能用动态规划的方式解决,首先看一个问题能不能由他的子问题进行解决。

这里假设一共有n个房间,每个房间的金额用数组nums[]来表示,求偷n个房间的最大值。 设: f(n)为偷到n个房间的最大值。

  • 最后一个房间只有偷和不偷两种方式

    • 偷:f(n)=f(n2)+nums[n]f(n) = f(n-2) + nums[n]
    • 不偷: f(n)=f(n1)f(n) = f(n-1)
  • 所以,n个房间的最大值是偷或者不偷中的最大值

    • f(n)=max(f(n2)+n,f(n1))f(n) = max(f(n-2) + n, f(n-1))
  • 基本思想如上所述,该问题可以由动态规划解法。

let rob = function(nums) {
  let len = nums.length;

  if(len === 0)
  return 0;

  if(len === 1)
  return nums[0];

  if(len === 2)
  return Math.max(nums[0], nums[1]);

  let dp = []; // 用数组来记录每到k个房间的最大值
  dp[0] = 0;
  dp[1] = nums[0];
  dp[2] = Math.max(nums[0], nums[1])
  for(let i = 3; i <= len; i++) {
    dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i-1]); // 用刚才推算出来的算法来进行计算
  }

  return dp[len]; //返回最后的值
}

仔细观察可以发现,算到n个房间只需要n-1n-2的最大值就可以了,也就是我们只需要保存前面两个情况的最大值。

就可以算出n个房间的最大值,所以我们只需要利用一个临时变量重复赋值就可以了,不需要用到数组。

这样我们的空间复杂度就由O(N)变成了O(1)

let rob = function(nums) {
  let len = nums.length;

  if(len === 0)
  return 0;

  if(len === 1)
  return nums[0];

  if(len === 2)
  return Math.max(nums[0], nums[1]);

  // temp记录每次到i的结果
  let temp;
  // 利用prev2记录i-2的最大值,prev1记录i-1的最大值。
  let prev2 = nums[0];
  let prev1 = Math.max(nums[0], nums[1])
  for(let i = 3; i <= len; i++) {
    temp = Math.max(prev1, prev2 + nums[i-1]);
    prev2 = prev1;
    prev1 = temp;
  }

  return temp;
}