198. (打家劫舍)House Robber
首先表示一下我的小激动,做动态规划题时,第一次完完全全自己想到的解决方法,虽然这是easy题,但说实话,前面的“爬楼梯”题,如果我不是之前看过剑指offer的解题方案,可能也不能直接手撕代码。现在竟然完完全全通过自己找到动态规划的状态方程,,而且只提交了两次就直接acc,无比开心,说明最近leetcode刷题的付出,是有收获的!今年(2018年)的秋招让我明白了自己与别人的差距,数据结构和算法薄弱,基础知识不牢固,技术掌握不精,没有接触新技术(redis、mq、分布式等),没找工作以前,一直以为自己会搭建个能跑通的小网站就是大牛了,现在发现我只是一只井底之蛙,没有见过世面,所以到头来网申连笔试都过不了,深受打击。因此希望自己奋发图强,笨鸟先飞,我相信勤能补拙,天道只酬勤!
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 。
2. 题目分析
题目说不能偷小相邻的,即,必须至少跨一间房,也可以跨两间房,但是,要想偷窃到最高金额,那么最多跨两间房,因为如果跨三间房,那么其实可以通过偷窃中间那间房,然后再偷目的地房间的。
如题目中的2,7,9,3,1。
我要偷第四间房的现金,如果跨三间房,那么就是从第一间房直接到第五间房,总共获得现金=2+1=3;
但如果我在第一间房,然后到第三间房,再到第五间房,那么不就多偷了一间房吗?所以最多跨两间房。
另外,需要注意的是,不要以为通过状态方程最后得到的dp[nums.length-1]就是最大值,因为dp[nums.length-2]有可能dp[nums.length-1]大。
3. 解决思路
状态:dp[i]表示偷完第i个房间所拿到的最大现金数。
状态转移方程:f(n) = max(f(n-3) , f(n-2)) + nums[i] (n >=3);
4. 代码实现(java)
- 递归方法实现
package com.algorithm.leetcode.dynamicAllocation;
/**
* Created by 凌 on 2018/12/12.
* 描述:198. House Robber
*/
public class Rob {
public static void main(String[] args) {
int[] nums = {2,7,9,3,5,4};
int result = rob(nums);
System.out.println(result);
}
public static int rob(int[] nums) {
//如果参数是数组,不仅要判断数组是否为空,而且还要判断数组长度是否为0,如参数[].
if (nums == null || nums.length == 0){
return 0;
}
int len = nums.length;
int[] dp = new int[len];
if (len == 1){
return nums[0];
}
dp[0] = nums[0];
dp[1] = nums[0] > nums[1]? nums[0]:nums[1];
if (len == 2){
return dp[1];
}
dp[2] = nums[0]+nums[2] > nums[1]? nums[0]+nums[2]:nums[1];
if (len == 3){
return dp[2];
}
for (int i = 3; i < len; i++) {
dp[i] = Math.max(dp[i-3], dp[i-2])+nums[i];
}
return dp[len-1]>dp[len-2] ? dp[len-1] : dp[len-2];
}
}