作者: 负雪明烛
id: fuxuemingzhu
个人博客: http://fuxuemingzhu.cn/


[LeetCode]

题目地址:https://leetcode.com/problems/house-robber/

Total Accepted: 67398 Total Submissions: 196356 Difficulty: Easy

题目描述

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.

题目大意

每个房间里有些价值的物品,不能偷连续的房间,那么求最多能偷多少物品?

解题方法

动态规划到底怎么想?其实可以先用 递归+记忆化 解决问题,然后再转化成动态规划。

首先说明的是 递归+记忆化从顶向下的一种解决方式:即我们要解决大问题,大问题拆解成小问题。
动态规划从底向上的一种解决方式:即我们先解决小问题,然后逐步推出大问题。

递归

假如dfs(i)表示从左到右的第 i 个位置能偷多少金额,是不是就是 max(dfs(i - 1), dfs(i - 2) + nums[i])。自顶向下的思路就是递归去求解 dfs(i - 1), dfs(i - 2)

所以我们有了最简单的一个递归代码:

class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
return self.dfs(nums, len(nums) - 1) # 在第 i 个房间之前(包括 i)能获取的最大收益
def dfs(self, nums, i):
if i == 0:
return nums[0]
if i == 1:
return max(nums[0], nums[1])
return max(self.dfs(nums, i - 1), self.dfs(nums, i - 2) + nums[i])

提交之后发现超时了。

递归 + 记忆化

为什么超时呢,是因为我们有重复计算:dfs(2) 需要求 dfs(0)dfs(1);而 dfs(3) 需要求 dfs(2),然后再求一遍 dfs(0)dfs(1)

解决这个问题的方法是:记录一下已经求过的值,避免重复计算。

于是有了记忆化的方法,用memo[i]记录已经求过的dfs(i),之后在搜索的时候,先找 memo中是否已经保存了这个数字,如果已经保存就不用再计算了。

于是有了以下的代码:

class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
self.memo = dict()
return self.dfs(nums, len(nums) - 1) # 在第 i 个房间之前(包括 i)能获取的最大收益
def dfs(self, nums, i):
if i in self.memo:
return self.memo[i]
res = 0
if i == 0:
res = nums[0]
elif i == 1:
res = max(nums[0], nums[1])
else:
res = max(self.dfs(nums, i - 1), self.dfs(nums, i - 2) + nums[i])
self.memo[i] = res
return res

这份答案已经能通过了。

动态规划

上面分析了这么多,可以看出递归是先想获得 i 位置的结果 ,然后分解成求解 i - 1 位置的结果 和 i - 2 位置的结果。这就是从顶向下

反过来我们也可以想到,如果先求 i - 1 位置的结果 和 i - 2 位置的结果,再求 i 位置的结果不是也行吗?对!这就是 动态规划,它的思想是从底向上

首先定义状态: dp[i] 表示从左到右的第 i 个位置能偷多少金额。(和递归的定义是不是一样?)

然后明确状态转移方程:

dp[0] = num[0] (当i=0时)
dp[1] = max(num[0], num[1]) (当i=1时)
dp[i] = max(num[i] + dp[i - 2], dp[i - 1]) (当 i !=0 and i != 1 时)

最后写代码:

class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
N = len(nums)
dp = [0] * (N + 1)
dp[1] = nums[0]
for i in range(1, N):
dp[i + 1] = max(dp[i], dp[i - 1] + nums[i])
return dp[-1]

动态规划会比递归 + 记忆化的速度更快,主要是递归 + 记忆化需要开辟栈空间,而且还需要多一步是否在 memo 中存在的判断。

Java代码如下:

public class Solution {
public int rob(int[] nums) {
if(nums.length==0) return 0;
if(nums.length==1) return nums[0];
int[] maxMoney=new int[nums.length];
maxMoney[0]=nums[0];
maxMoney[1]=Math.max(nums[0],nums[1]);
for(int i=2; i<nums.length; i++){
maxMoney[i]=Math.max(nums[i]+maxMoney[i-2], maxMoney[i-1]);
}
return maxMoney[nums.length-1];
}
}

AC:0ms

优化动态规划空间

我们看到动态规划的解法中,dp[i] 只和 dp[i - 1]dp[ i - 2] 有关,因此可以用变量优化使用空间:

Python代码如下:

class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
prev, cur = 0, 0
for value in nums:
prev, cur = cur, max(prev + value, cur)
return cur

日期

2016/5/1 21:44:42
2018 年 9 月 9 日
2018 年 11 月 21 日 —— 又是一个美好的开始
2020 年 5 月 29 日 —— 答辩顺利

【LeetCode】198. House Robber 打家劫舍 解题报告(Java & Python)的更多相关文章

  1. 【LeetCode】383. Ransom Note 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Java解法 Python解法 日期 [LeetCo ...

  2. 【LeetCode】575. Distribute Candies 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Java解法 Python解法 日期 题目地址:ht ...

  3. 【LeetCode】136. Single Number 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 异或 字典 日期 [LeetCode] 题目地址:h ...

  4. 【LeetCode】283. Move Zeroes 解题报告(Java & Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:首尾指针 方法二:头部双指针+双循环 方法三 ...

  5. 【LeetCode】386. Lexicographical Numbers 解题报告(Python)

    [LeetCode]386. Lexicographical Numbers 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博 ...

  6. 【LeetCode】376. Wiggle Subsequence 解题报告(Python)

    [LeetCode]376. Wiggle Subsequence 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.c ...

  7. 【LeetCode】649. Dota2 Senate 解题报告(Python)

    [LeetCode]649. Dota2 Senate 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地 ...

  8. 【LeetCode】911. Online Election 解题报告(Python)

    [LeetCode]911. Online Election 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ ...

  9. 【LeetCode】886. Possible Bipartition 解题报告(Python)

    [LeetCode]886. Possible Bipartition 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu ...

随机推荐

  1. Demo04分解质因数

    package 习题集1;import java.util.Scanner;//将一个正整数分解质因数.例如输入90,打印出90=2*3*3*5public class Demo04 { public ...

  2. 振鹏同学正式学习java的第一天!

    一.今日收获 1.最棒的莫过于运行Java的HelloWorld! 2.在同学的帮助下历经坎坷困苦安装完成了Eclipse软件并设置好环境变量. 3.最最最开始了解了Java的前世今生,编程语言发展的 ...

  3. 零基础学习java------day18------properties集合,多线程(线程和进程,多线程的实现,线程中的方法,线程的声明周期,线程安全问题,wait/notify.notifyAll,死锁,线程池),

    1.Properties集合 1.1 概述: Properties类表示了一个持久的属性集.Properties可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串 一个属性列表可包含另 ...

  4. 源码分析-Producer

    消息生产者的代码都在client模块中,相对于RocketMQ来讲,消息生产者就是客户端,也是消息的提供者. 方法和属性 主要方法介绍 //创建主题 void createTopic(final St ...

  5. [php代码审计] 通读审计之shangfancms

    前言 大部分的MVC框架,访问的控制器大部分是由外部参数来决定的,那么本篇文章所通读的MVC框架与之前的一系列MVC框架不太一样,它的路由是由程序本身的路由表来决定的. 源码下载 https://ww ...

  6. Oracle中dbms_random包详解

    Oracle之DBMS_RANDOM包详解参考自:https://www.cnblogs.com/ivictor/p/4476031.html https://www.cnblogs.com/shen ...

  7. [学习总结]8、android 自定义控件 使用declare-styleable进行配置属性(源码角度)

    declare-styleable:declare-styleable是给自定义控件添加自定义属性用的. 官方的相关内部控件的配置属性文档:http://developer.android.com/r ...

  8. 【Linux】【Services】【nfs】nfs安装与配置

    1. 概念 1.1. NFS:Network File System,传统意义上,文件系统在内核中实现. 1.2. RPC:Remote Procedure Call protocol,远程过程调用, ...

  9. 阿里云esc 安装 docker

    1. 更新 yum 到最新: yum update (用 root 用户登录,无需加 sudo,如果不是,需要加,即  yum update ) 2. 安装软件包:yum-util(提供 yum-co ...

  10. 【力扣】337. 打家劫舍 III

    在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区.这个地区只有一个入口,我们称之为"根". 除了"根"之外,每栋房子有且只有一个" ...