198.打家劫舍
解题思路:
因为不能偷两个相邻的房间,偷到当前房间能偷的最大价值就转化成了两种情况:
1.偷第i个房间,那么第i-1个房间肯定就不能偷了,当前情况的最大价值就是偷到i-2房间的最大价值+当前房间的财产价值。
2.不偷第i个房间,那么第i-1个房间可以偷,所以当前情况的最大价值就是偷到i-1房间的最大价值。
class Solution {
public int rob(int[] nums) {
/**
* dp[i] 从下标0偷到下标j能偷的最大金额
* dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i])
*/
if(nums.length == 1) return nums[0];
int[] dp = new int[nums.length + 1];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++){
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
for(int k = 0; k < dp.length; k++){
System.out.print(dp[k] + " ");
}
System.out.println();
}
return dp[nums.length - 1];
}
}
213.打家劫舍II
题目链接:213. 打家劫舍 II - 力扣(LeetCode)
解题思路:
class Solution {
public int rob(int[] nums) {
/**
* 该问题可以分为两种情况考虑,因为房子是一个环,第一个房间和最后一个房间不可能同时偷,所以分为一下两种情况:
* 1.偷第一个房子,最后一个房子肯定不偷了,直接将其剔除
* 2.偷最后一个房子,第一个房子肯定不偷了,将其剔除
* 后续两种情况分别按照打家劫舍I处理即可。
* dp[i] 前j个房子能偷的最大金额
* dp[i] = Math.maxx(dp[i - 1], dp[i - 2] + nums[i]);
* dp[start] = nums[start]
* dp[start + 1] = Math.max(nums[start], nums[start + 1])
*/
if(nums.length == 1) return nums[0];
int hasStart = maxprice(nums,0,nums.length - 2);
int hasEnd = maxprice(nums, 1, nums.length - 1);
return Math.max(hasEnd,hasStart);
}
public int maxprice(int[] nums, int startIndex, int endIndex){
if(startIndex == endIndex) return nums[startIndex];
int[] dp = new int[nums.length + 1];
dp[startIndex] = nums[startIndex];
dp[startIndex + 1] = Math.max(nums[startIndex], nums[startIndex + 1]);
for(int i = startIndex + 2; i <= endIndex; i++){
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[endIndex];
}
}
337.打家劫舍III
题目链接:337. 打家劫舍 III - 力扣(LeetCode)
解题思路:
这个题有很多点需要注意和学习,需要多次复习。
递归写法,这种类型返回值的递归要好好留意。
class Solution {
public int rob(TreeNode root) {
/**
* 第一次接触树形结构的dp,有点懵逼
* 状态标记递归写法,当递归想要返回多个值的时候,可以考虑返回一个数组
*/
List<Integer> record = maxPrice(root);
return Math.max(record.get(0),record.get(1));
}
List<Integer> maxPrice(TreeNode root){
if(root == null) return Arrays.asList(0,0);
// 自底向上,后序遍历
List<Integer> left = maxPrice(root.left); // 左
List<Integer> right = maxPrice(root.right); // 右
// 中
int val1 = 0, val2 = 0; // 1:偷 2:不偷
val1 = root.val + left.get(1) + right.get(1);
val2 += Math.max(left.get(0),left.get(1)) + Math.max(right.get(0), right.get(1));
return Arrays.asList(val1,val2);
}
}
注意两种不同返回方式的写法,int[]和ArrayList()
class Solution {
public int rob(TreeNode root) {
/**
* 第一次接触树形结构的dp,有点懵逼
* 状态标记递归写法,当递归想要返回多个值的时候,可以考虑返回一个数组
*/
int[] record = maxPrice(root);
return Math.max(record[0],record[1]);
}
int[] maxPrice(TreeNode root){
if(root == null) return new int[]{0,0};
// 自底向上,后序遍历
int[] left = maxPrice(root.left); // 左
int[] right = maxPrice(root.right); // 右
// 中
int val1 = 0, val2 = 0; // 1:偷 2:不偷
val1 = root.val + left[1] + right[1];
val2 += Math.max(left[0],left[1]) + Math.max(right[0], right[1]);
return new int[]{val1,val2};
}
}
记忆化递归,其实就是用一个Map来存储已经计算过的根节点对应的值,避免重复计算。
class Solution {
public int rob(TreeNode root) {
/**
* 记忆化递归
*/
Map<TreeNode,Integer> record = new HashMap<>();
int ans = maxPrice(root, record);
return ans;
}
int maxPrice(TreeNode root, Map<TreeNode, Integer> record){
if(root == null) return 0;
if(record.containsKey(root)) return record.get(root);
int val1 = 0, val2 = root.val;
// 偷
if(root.left != null) val2 += maxPrice(root.left.right,record) + maxPrice(root.left.left,record);
if(root.right != null) val2 += maxPrice(root.right.left,record) + maxPrice(root.right.right,record);
// 不偷
val1 = maxPrice(root.left,record) + maxPrice(root.right,record);
int ans = Math.max(val1, val2);
record.put(root,ans);
return ans;
}
}