198. 打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 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 。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 400
方法:动态规划
-
定义状态 f[i][j] : 表示到第 i 个房间 ,状态为j(j == 0表示不选,j == 1表示选)时的最大价值。
-
定义状态转移方程
f [ i ] [ 0 ] = m a x ( f [ i − 1 ] [ 1 ] , f [ i − 1 ] [ 0 ] ) f[i][0]=max(f[i-1][1],f[i-1][0]) f[i][0]=max(f[i−1][1],f[i−1][0])
f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] + n u m s [ i ] f[i][1]=f[i-1][0]+nums[i] f[i][1]=f[i−1][0]+nums[i]
下附代码👇
class Solution {
public int rob(int[] nums) {
int n =nums.length;
//俩个边界判断
if(n==0) return 0;
if(n==1) return nums[0];
//定义状态
int [][] f =new int[n][2];
//给起点赋初值
f[0][0]=0;
f[0][1]=nums[0];
for(int i=1;i<n;i++){
f[i][0]=Math.max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+nums[i];
}
//最终答案在选和不选最后一个房间中找寻最大价值
return Math.max(f[n-1][0],f[n-1][1]);
}
}
213. 打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [0]
输出:0
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
Ⅱ对于Ⅰ来说多增加了前后房间相邻的条件,也就是说,如果选了第一间房那么最后一间将无法再选,反之可以选择。
使用我们在此时应该分情况对 第一间房和最后一间房的选取与否分情况讨论。
- 选取 第一间房 的情况:
f[0][0]=0
f[0][1]=nums[0]
此时最大价值应该为 m a x ( f [ n − 2 ] [ 1 ] , f [ n − 2 ] [ 0 ] ) max(f[n-2][1],f[n-2][0]) max(f[n−2][1],f[n−2][0]) - 不选取 第一间房 的情况:
f[0][0]=0
f[0][1]=0
此时最大价值应该为 m a x ( f [ n − 2 ] [ 1 ] , f [ n − 2 ] [ 0 ] + n u m s [ n − 1 ] ) max(f[n-2][1],f[n-2][0]+nums[n-1]) max(f[n−2][1],f[n−2][0]+nums[n−1])
最终结果应该为 m a x ( 选 取 第 一 间 房 , 不 选 取 第 一 件 房 ) max(选取第一间房,不选取第一件房) max(选取第一间房,不选取第一件房)
class Solution {
public int rob(int[] nums) {
int n =nums.length;
if(n==0) return 0;
if(n==1) return nums[0];
int [][] f =new int[n][2];
//不选取第一间房
f[0][0]=0;
f[0][1]=0;
for(int i=1;i<n-1;i++){
f[i][0]=Math.max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+nums[i];
}
int ans = Math.max(f[n-2][1],f[n-2][0]+nums[n-1]);
//选取第一间房
f[0][0]=0;
f[0][1]=nums[0];
for(int i=1;i<n-1;i++){
f[i][0]=Math.max(f[i-1][0],f[i-1][1]);
f[i][1]=f[i-1][0]+nums[i];
}
ans = Math.max(ans,Math.max(f[n-2][1],f[n-2][0]));
return ans;
}
}
当然根据观察会发现,起始f[i][j]的值只依赖 f[i-1][j],所以可以把空间缩小为o(1)
class Solution {
public int rob(int[] nums) {
int n =nums.length;
if(n==0) return 0;
if(n==1) return nums[0];
int [][] f =new int[2][2];
//不选第一间房
f[0][0]=0;
f[0][1]=0;
for(int i=1;i<n-1;i++){
f[i%2][0]=Math.max(f[(i-1)%2][0],f[(i-1)%2][1]);
f[i%2][1]=f[(i-1)%2][0]+nums[i];
}
int ans = Math.max(f[(n-2)%2][1],f[(n-2)%2][0]+nums[n-1]);
//选第一间房
f[0][0]=0;
f[0][1]=nums[0];
for(int i=1;i<n-1;i++){
f[i%2][0]=Math.max(f[(i-1)%2][0],f[(i-1)%2][1]);
f[i%2][1]=f[(i-1)%2][0]+nums[i];
}
ans = Math.max(ans,Math.max(f[(n-2)%2][1],f[(n-2)%2][0]));
return ans;
}
}