198 打家劫舍
令
D
[
i
]
D[i]
D[i]表示前
i
i
i间房子的最大收益:
D
[
i
]
=
max
(
D
[
i
−
1
]
,
D
[
i
−
2
]
+
n
u
m
s
[
i
]
)
D
[
0
]
=
n
u
m
s
[
0
]
D
[
1
]
=
max
(
n
u
m
s
[
0
]
,
n
u
m
s
[
1
]
)
D[i] = \max(D[i -1], D[i-2]+nums[i]) \\ D[0] = nums[0] \\ D[1] = \max(nums[0], nums[1])
D[i]=max(D[i−1],D[i−2]+nums[i])D[0]=nums[0]D[1]=max(nums[0],nums[1])
213 打家劫舍Ⅱ
围成一圈,能偷盗的最高金额。
- 情况1:不偷0、偷n-1
- 情况2:不偷n-1、偷0
- 情况3:既不偷0,也不偷n-1
如何保证第一间房屋和最后一间房屋不同时偷窃呢?如果偷窃了第一间房屋,则不能偷窃最后一间房屋,因此偷窃房屋的范围是第一间房屋到最后第二间房屋;如果偷窃了最后一间房屋,则不能偷窃第一间房屋,因此偷窃房屋的范围是第二间房屋到最后一间房屋。
即取下面二者中的较大值。显然这两个式子在情况3是有重复的,但无所谓,只要覆盖了情况3就好,就可以求出最大值。
rob
(
n
u
m
s
[
0
,
n
−
2
]
)
⇒
case2 + case3
rob
(
n
u
m
s
[
1
,
n
−
1
]
)
⇒
case1 + case3
\text{rob}(nums[0,n-2])\Rightarrow \text{ case2 + case3} \\ \text{rob}(nums[1,n-1])\Rightarrow \text{ case1 + case3}
rob(nums[0,n−2])⇒ case2 + case3rob(nums[1,n−1])⇒ case1 + case3
class Solution {
public int rob(int[] nums, int l, int r) {
// 偷盗nums[l,r]范围
if (l > r) {
return 0;
}
if (l == r) {
return nums[l];
}
int[] D = new int[r - l + 1];
D[0] = nums[l];
D[1] = Math.max(nums[l], nums[l + 1]);
for (int i = 2; i < r - l + 1; i++) {
D[i] = Math.max(D[i - 1], D[i - 2] + nums[l + i]);
}
return D[r - l];
}
public int rob(int[] nums) {
if (nums.length == 1) {
return nums[0];
}
int res1 = rob(nums, 0, nums.length - 2);
int res2 = rob(nums, 1, nums.length - 1);
return Math.max(res1, res2);
}
}
337 打家劫舍Ⅲ★
树,能偷盗的最高金额。
int rob(TreeNode root)
:
【一个节点具有两个属性值:(1)抢劫该节点能够得到的最大收益、(2)不抢劫该节点能得到的最大收益】
- 抢劫该节点,则不能抢劫两个孩子节点
- 不抢劫该节点,可以抢劫两个孩子节点,也可以不抢劫两个孩子节点,取其中更大的
class Solution {
public int rob(TreeNode root) {
int[] money = robOrNot(root);
return Math.max(money[0], money[1]);
}
public int[] robOrNot(TreeNode root) {
if (root == null) {
return new int[]{0, 0};
}
int[] money = new int[2];
int[] left = robOrNot(root.left);
int[] right = robOrNot(root.right);
// 不抢劫该节点
money[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
// 抢劫该节点
money[1] = left[0] + right[0] + root.val;
return money;
}
}