打劫房屋 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.csdn.net/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.csdn.net/zaqwsx20/article/details/70156192 (沒看懂)