前言
最近一直在看动态规划的内容,看了不少的例题,比如LCS, LIS, 硬币还有01背包。但是抛开例题,让自己上手写还是写不出来,尤其是状态转移方程嚼劲脑汁也写不出来。
leetcode中关于动态规划的题目,多是中等,困难的题目。简单题目的数量一只手都能数过来。本题也是我自己从0到1,自己思考出来的第一道动态规划的题目。虽然只是一个简单题,但是依然是可喜可贺。😊
原题
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 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 。
思路
对于第i家,我们可以选择打劫或者不打劫。
如果选择打劫,那么偷窃到的金额等于dp[i-2] + vi(上上家时盗窃的金额加上本次盗窃的金额)
如果选择不打劫,那么盗窃的金额等于dp[i-1](上家时盗窃的金额)。我们取其中的最大值即可。
我们根据分析可以列出下面的状态转移方程
我们无需关心第i家的状态,我们只需要关心第i-1和i-2家的状态即可。这个性质叫做最优子结构。
同理我们无需关心第i-1和i-2的状态,只需要关心i-2和i-4的状态即可。依次向前推即可。这个性质叫做无后效性。
代码
/**
* @param {number[]} nums
* @return {number}
*/
var rob = function(nums) {
if (nums.length === 0) {
return 0
}
if (nums.length === 1) {
return nums[0]
}
const dp = []
for (let i = 0; i < nums.length; i++) {
let m = dp[i - 2] === undefined ? 0 : dp[i - 2]
m += nums[i]
let n = dp[i - 1] === undefined ? 0 : dp[i - 1]
dp[i] = Math.max(m, n)
}
return Math.max(dp[dp.length - 1], dp[dp.length - 2])
};