【LeetCode】198. House Robber 打家劫舍 解题报告(Java & Python)
作者: 负雪明烛
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)的更多相关文章
- 【LeetCode】383. Ransom Note 解题报告(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Java解法 Python解法 日期 [LeetCo ...
- 【LeetCode】575. Distribute Candies 解题报告(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 Java解法 Python解法 日期 题目地址:ht ...
- 【LeetCode】136. Single Number 解题报告(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 异或 字典 日期 [LeetCode] 题目地址:h ...
- 【LeetCode】283. Move Zeroes 解题报告(Java & Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 方法一:首尾指针 方法二:头部双指针+双循环 方法三 ...
- 【LeetCode】386. Lexicographical Numbers 解题报告(Python)
[LeetCode]386. Lexicographical Numbers 解题报告(Python) 标签(空格分隔): LeetCode 作者: 负雪明烛 id: fuxuemingzhu 个人博 ...
- 【LeetCode】376. Wiggle Subsequence 解题报告(Python)
[LeetCode]376. Wiggle Subsequence 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.c ...
- 【LeetCode】649. Dota2 Senate 解题报告(Python)
[LeetCode]649. Dota2 Senate 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地 ...
- 【LeetCode】911. Online Election 解题报告(Python)
[LeetCode]911. Online Election 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ ...
- 【LeetCode】886. Possible Bipartition 解题报告(Python)
[LeetCode]886. Possible Bipartition 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu ...
随机推荐
- Session和Cookie的原理,以及在分布式应用中出现的问题和解决方案
产生原因 由于http协议是无状态的,同一个浏览器对服务器的两次请求之间是没有关系的,服务器认为两次请求都是全新的请求,不会记住上次请求成功的数据.然而现有的业务常常需要服务器能记住用户的访问情况, ...
- perl substr
substr EXPR,OFFSET,LENGTH,REPLACEMENT substr EXPR,OFFSET,LENGTH substr EXPR,OFFSET Extracts a substr ...
- 自动添加shell脚本头部信息
autocmd BufNewFile *.sh exec ":call AddTitleForShell()" function AddTitleForShell() call a ...
- C++栈溢出
先看一段代码 #include<iostream> using namespace std; #define n 510 void sum(int a,int b) { cout<& ...
- A Child's History of England.1
A Child's History of England, by Charles Dickens (狄更斯) CHAPTER I ANCIENT ENGLAND AND THE ROMANS If y ...
- 一起手写吧!Promise!
1.Promise 的声明 首先呢,promise肯定是一个类,我们就用class来声明. 由于new Promise((resolve, reject)=>{}),所以传入一个参数(函数),秘 ...
- c#中实现串口通信的几种方法
c#中实现串口通信的几种方法 通常,在C#中实现串口通信,我们有四种方法: 第一:通过MSCOMM控件这是最简单的,最方便的方法.可功能上很难做到控制自如,同时这个控件并不是系统本身所带,所以还得注册 ...
- Java SSLSocket
Java SSLSocket JSSE(Java Security Socket Extension)是Sun公司为了解决互联网信息安全传输提出的一个解决方案,它实现了SSL和TSL协议,包含了数据加 ...
- JavaIO——System对IO的支持、序列化
1.系统类对IO的支持 在我们学习PriteWriter.PrintStream里面的方法print.println的时候是否观察到其与我们之前一直使用的系统输出很相似呢?其实我们使用的系统输出就是采 ...
- GO Exit Fatal panic
Exit() 应用程序(不只是函数)退出执行 defer 不会被执行(因为程序都退出了) log.Fatal() 输出打印内容 应用程序退出 defer 不会被执行 panic() 函数停止执行(不是 ...