LeetCode 198 - House Robber
LeetCode 213 - House Robber II
LeetCode 373 - House Robber III
[LeetCode]198.House Robber - Yoona - 博客频道 - CSDN.NET
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.
https://leetcode.com/problems/house-robber/discuss/156523/From-good-to-great.-How-to-approach-most-of-DP-problems.
这道题的本质相当于在一列数组中取出一个或多个不相邻数,使其和最大。那么我们对于这类求极值的问题首先考虑动态规划Dynamic Programming来解,我们维护一个一位数组dp,其中dp[i]表示到i位置时不相邻数能形成的最大和,那么状态转移方程怎么写呢,我们先拿一个简单的例子来分析一下,比如说nums为{3, 2, 1, 5},那么我们来看我们的dp数组应该是什么样的,首先dp[0]=3没啥疑问,再看dp[1]是多少呢,由于3比2大,所以我们抢第一个房子的3,当前房子的2不抢,所以dp[1]=3,那么再来看dp[2],由于不能抢相邻的,所以我们可以用再前面的一个的dp值加上当前的房间值,和当前房间的前面一个dp值比较,取较大值当做当前dp值,所以我们可以得到状态转移方程dp[i] = max(num[i] + dp[i - 2], dp[i - 1]), 由此看出我们需要初始化dp[0]和dp[1],其中dp[0]即为num[0],dp[1]此时应该为max(num[0], num[1])
用本身数组代替dp数组。
int rob(vector<int> &num) { if(num.empty()){ return 0; }//if int size = num.size(); num[1] = max(num[0],num[1]); for(int i = 2;i < size;++i){ num[i] = max(num[i-1],num[i-2]+num[i]); }//for return num[size-1]; }
public int rob(int[] nums) { int r = 0, nr = 0; for (int i = 0; i < nums.length; i++) { int t = r; r = Math.max(r, nr + nums[i]); nr = t; } return Math.max(r, nr); }
经典的小偷问题dp
经典的小偷问题:一排房子,每个房子里有一定价值的东西,小偷不能偷相邻的两
个房间。即如果小偷光临了房间i, 那么就不能再偷房间i – 1和房间i + 1。要求返回
小偷能偷到东西的总价值的最大值
Read full article from [LeetCode]198.House Robber - Yoona - 博客频道 - CSDN.NET
LeetCode 213 - House Robber II
LeetCode 373 - House Robber III
[LeetCode]198.House Robber - Yoona - 博客频道 - CSDN.NET
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.
https://leetcode.com/problems/house-robber/discuss/156523/From-good-to-great.-How-to-approach-most-of-DP-problems.
There is some frustration when people publish their perfect fine-grained algorithms without sharing any information abut how they were derived. This is an attempt to change the situation. There is not much more explanation but it's rather an example of higher level improvements. Converting a solution to the next step shouldn't be as hard as attempting to come up with perfect algorithm at first attempt.
This particular problem and most of others can be approached using the following sequence:
- Find recursive relation
- Recursive (top-down)
- Recursive + memo (top-down)
- Iterative + memo (bottom-up)
- Iterative + N variables (bottom-up)
Step 1. Figure out recursive relation.
A robber has 2 options: a) rob current house
If an option "a" is selected it means she can't rob previous
If an option "b" is selected the robber gets all the possible loot from robbery of
So it boils down to calculating what is more profitable:
A robber has 2 options: a) rob current house
i
; b) don't rob current house.If an option "a" is selected it means she can't rob previous
i-1
house but can safely proceed to the one before previous i-2
and gets all cumulative loot that follows.If an option "b" is selected the robber gets all the possible loot from robbery of
i-1
and all the following buildings.So it boils down to calculating what is more profitable:
- robbery of current house + loot from houses before the previous
- loot from the previous house robbery and any loot captured before that
rob(i) = Math.max( rob(i - 2) + currentHouseValue, rob(i - 1) )
Step 2. Recursive (top-down)
Converting the recurrent relation from Step 1 shound't be very hard.
Converting the recurrent relation from Step 1 shound't be very hard.
public int rob(int[] nums) {
return rob(nums, nums.length - 1);
}
private int rob(int[] nums, int i) {
if (i < 0) {
return 0;
}
return Math.max(rob(nums, i - 2) + nums[i], rob(nums, i - 1));
}
This algorithm will process the same
i
multiple times and it needs improvement. Time complexity: [to fill]
Step 3. Recursive + memo (top-down).
int[] memo;
public int rob(int[] nums) {
memo = new int[nums.length + 1];
Arrays.fill(memo, -1);
return rob(nums, nums.length - 1);
}
private int rob(int[] nums, int i) {
if (i < 0) {
return 0;
}
if (memo[i] >= 0) {
return memo[i];
}
int result = Math.max(rob(nums, i - 2) + nums[i], rob(nums, i - 1));
memo[i] = result;
return result;
}
Much better, this should run in
O(n)
time. Space complexity is O(n)
as well, because of the recursion stack, let's try to get rid of it.
Step 4. Iterative + memo (bottom-up)
public int rob(int[] nums) {
if (nums.length == 0) return 0;
int[] memo = new int[nums.length + 1];
memo[0] = 0;
memo[1] = nums[0];
for (int i = 1; i < nums.length; i++) {
int val = nums[i];
memo[i+1] = Math.max(memo[i], memo[i-1] + val);
}
return memo[nums.length];
}
Step 5. Iterative + 2 variables (bottom-up)
We can notice that in the previous step we use only
We can notice that in the previous step we use only
memo[i]
and memo[i-1]
, so going just 2 steps back. We can hold them in 2 variables instead. This optimization is met in Fibonacci sequence creation and some other problems [to paste links]./* the order is: prev2, prev1, num */
public int rob(int[] nums) {
if (nums.length == 0) return 0;
int prev1 = 0;
int prev2 = 0;
for (int num : nums) {
int tmp = prev1;
prev1 = Math.max(prev2 + num, prev1);
prev2 = tmp;
}
return prev1;
}
http://www.cnblogs.com/grandyang/p/4383632.html这道题的本质相当于在一列数组中取出一个或多个不相邻数,使其和最大。那么我们对于这类求极值的问题首先考虑动态规划Dynamic Programming来解,我们维护一个一位数组dp,其中dp[i]表示到i位置时不相邻数能形成的最大和,那么状态转移方程怎么写呢,我们先拿一个简单的例子来分析一下,比如说nums为{3, 2, 1, 5},那么我们来看我们的dp数组应该是什么样的,首先dp[0]=3没啥疑问,再看dp[1]是多少呢,由于3比2大,所以我们抢第一个房子的3,当前房子的2不抢,所以dp[1]=3,那么再来看dp[2],由于不能抢相邻的,所以我们可以用再前面的一个的dp值加上当前的房间值,和当前房间的前面一个dp值比较,取较大值当做当前dp值,所以我们可以得到状态转移方程dp[i] = max(num[i] + dp[i - 2], dp[i - 1]), 由此看出我们需要初始化dp[0]和dp[1],其中dp[0]即为num[0],dp[1]此时应该为max(num[0], num[1])
状态转移方程:
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时)
int rob(vector<int> &num) {
if(num.empty()){
return 0;
}//if
int size = num.size();
vector<int> dp(size,0);
// dp[i]=max(num[i]+dp[i-2],dp[i-1])
// dp[i]表示[0,i]取1个或多个不相邻数值的最大收益
dp[0] = num[0];
dp[1] = max(num[0],num[1]);
for(int i = 2;i < size;++i){
dp[i] = max(dp[i-1],dp[i-2]+num[i]);
}//for
return dp[size-1];
}用本身数组代替dp数组。
int rob(vector<int> &num) { if(num.empty()){ return 0; }//if int size = num.size(); num[1] = max(num[0],num[1]); for(int i = 2;i < size;++i){ num[i] = max(num[i-1],num[i-2]+num[i]); }//for return num[size-1]; }
public int rob(int[] nums) { int r = 0, nr = 0; for (int i = 0; i < nums.length; i++) { int t = r; r = Math.max(r, nr + nums[i]); nr = t; } return Math.max(r, nr); }
经典的小偷问题:一排房子,每个房子里有一定价值的东西,小偷不能偷相邻的两
个房间。即如果小偷光临了房间i, 那么就不能再偷房间i – 1和房间i + 1。要求返回
小偷能偷到东西的总价值的最大值
//solution: dp, two rows stands for the max value if steal the current one or not.
int
mostSteal(vector<
int
> &value){
int
size = value.size();
if
(size == 0)
return
0;
if
(size == 1)
return
value[0];
vector<vector<
int
> > dp(2, vector<
int
>(size, 0));
for
(
int
i = 1; i < size; i++){
dp[0][i] = dp[1][i-1]+value[i];
dp[1][i] = max(dp[0][i-1], dp[1][i-1]);
}
return
max(dp[0][size-1], dp[1][size-1]);
}