打劫房屋 I
思路:
采用动态规划思想,小偷从第一个房子开始依次向后移动,设截止到第i个房子时的最大获利为f(i)。
移动到第 i 个房子时,有两种选择:打劫第i个房屋,则第i-1个房子不能打劫,因此截止目前获利为 money[ i ] + f(i-2);不打劫第i个房子,则第i-1个房子可以打劫,所以截止目前获利为f(i-1)。到底要不要打劫第i个房子取决于二者获利谁更大,所以有f[n]=max( f[n-1], f[n-2]+money[n] )。
代码:
public class Solution {
/**
* @param A: An array of non-negative integers
* @return: The maximum amount of money you can rob tonight
*/
public long houseRobber(int[] A) {
// write your code here
int n = A.length;
if(n == 0)
return 0;
if(n == 1)
return A[0];
long[] dp = new long[n];
dp[0] = A[0]; //打劫至第一个房子的最大收益
dp[1] = Math.max(A[0], A[1]); //打劫至第二个房子的最大收益
for(int i = 2; i < n; i++) {
dp[i] = Math.max(dp[i-1], dp[i-2] + A[i]); //打劫至第i+1个房子的最大收益
}
return dp[n-1];
}
}
因为dp[n]只与dp[n-1]和dp[n-2]有关,因此只保存两个变量即可。
代码优化为:
public long houseRobber(int[] A) {
// write your code here
int n = A.length;
long pre2 = 0, pre1 = 0;
long ans = 0;
for(int i = 0; i < n; i++) {
ans = Math.max(pre2 + A[i], pre1);
pre2 = pre1;
pre1 = ans;
}
return ans;
}
tip:这样写就不用单独考虑n=0和n=1的情况。
参考:https://blog.youkuaiyun.com/ljlstart/article/details/48413325
打劫房屋 II
思路:与上一题相比,区别在于由直线变成了环形,第一个房子和最后一个房子不能同时打劫。假设房子编号1~n,分两种情况处理:
第一种,放弃最后一间房子,只打劫编号 1~ (n-1) 的房子,最大收益记为a;
第二种,放弃第一间房子,只打劫编号 2 ~ n 的房子,最大收益记为b。
则 a , b 中的较大值即为答案。
代码:
public class Solution {
/**
* @param nums: An array of non-negative integers.
* @return: The maximum amount of money you can rob tonight
*/
public int houseRobber2(int[] nums) {
// write your code here
int n = nums.length;
if(n == 0)
return 0;
if(n == 1)
return nums[0];
int a = sub(nums, 0, n-2); //放弃最后一间房子
int b = sub(nums, 1, n-1); //放弃第一间房子
return Math.max(a, b);
}
//打劫范围从nums[start]到nums[end],须保证start <= end
private static int sub(int[] nums, int start, int end) {
int n = nums.length;
int pre2 = 0, pre1 = 0;
int ans = 0;
for(int i = start; i <= end; i++) {
ans = Math.max(pre2 + nums[i], pre1);
pre2 = pre1;
pre1 = ans;
}
return ans;
}
}
打劫房屋 III
方法一:
这道题和上两题不同,不能用动态规划,而是用递归。
public class Solution {
/**
* @param root: The root of binary tree.
* @return: The maximum amount of money you can rob tonight
*/
// 递归方法
Map<TreeNode, Integer> map = new HashMap<>(); //避免重复计算
//打劫以root为根节点的二叉树,返回最大收益钱数
public int houseRobber3(TreeNode root) {
// write your code here
if(root == null)
return 0;
if(map.containsKey(root))
return map.get(root);
//打劫根节点,总钱数记为a
int a = root.val;
if(root.left != null) {
a += houseRobber3(root.left.left);
a += houseRobber3(root.left.right);
}
if(root.right != null) {
a += houseRobber3(root.right.left);
a += houseRobber3(root.right.right);
}
//不打劫根节点,总钱数记为b
int b = houseRobber3(root.left) + houseRobber3(root.right);
map.put(root, Math.max(a,b));
return Math.max(a, b); //返回a和b中的较大值
}
}
方法二:
http://www.aichengxu.com/other/6568545.htm
方法三:
https://blog.youkuaiyun.com/zaqwsx20/article/details/70156192 (没看懂)