作者: 负雪明烛
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. php header下载文件 无法查看原因

    php header下载文件 无法查看原因 php header下载文件 下方函数可以下载单个文件 function download($file_url){ if(!isset($file_url) ...

  2. 19. 删除链表的倒数第 N 个结点

    目录 19.删除链表的倒数第N个节点 题目 题解-暴力 题解-哈希表 题解-双指针 19.删除链表的倒数第N个节点 题目 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点. 输入:he ...

  3. 一个专业处理字符串的IDEA插件

    字符串处理想必是小伙伴们平时开发时经常碰到的一个 "难题".为什么要打上引号?因为你说他难吧,其实也不是什么特别复杂的事:你说他不难吧,弄起来还真挺麻烦的,像删除其中空行啊.切换大 ...

  4. Jenkins:参数化构建:分支|模块|回滚|打印日志

    @ 目录 多分支 安装Git Parameter Plug-In 配置参数 选择构建分支 分模块 前提 分模块build 参数配置 分模块shell脚本 mvn 的基本用法 分模块运行 Jenkins ...

  5. flink-----实时项目---day05-------1. ProcessFunction 2. apply对窗口进行全量聚合 3使用aggregate方法实现增量聚合 4.使用ProcessFunction结合定时器实现排序

    1. ProcessFunction ProcessFunction是一个低级的流处理操作,可以访问所有(非循环)流应用程序的基本构建块: event(流元素) state(容错,一致性,只能在Key ...

  6. 【MarkDown】--使用教程

    MarkDown使用教程 目录 MarkDown使用教程 一. 常用设置 1.1 目录 1.2 标题 1.3 文本样式 (1)引用 (2)高亮 (3)强调 (4)水平线 (5)上下标 (6)插入代码 ...

  7. h5移动端设备像素比dpr介绍

    首先介绍一下概念 devicePixelRatio其实指的是window.devicePixelRatio window.devicePixelRatio是设备上物理像素和设备独立像素(device- ...

  8. 【1】Embarrassingly Parallel(易并行计算问题)

    1.什么是Embarrassingly Parallel(易并行计算问题) 易并行计算问题:A computation that can be divided into a number of  co ...

  9. Linux之sftp服务

    Linux之sftp服务 一.sftp介绍转自:[1]Linux如何开启SFTP https://www.cnblogs.com/xuliangxing/p/7120205.htmlSFTP是Secu ...

  10. LinkBinTree

    package ch11; import java.util.ArrayList; import java.util.List; import java.util.Stack; public clas ...