这两题都做错了T_T,所以参考了别人的。代码来源:地址
II题目:
在上次打劫完一条街道之后,窃贼又发现了一个新的可以打劫的地方,但这次所有的房子围成了一个圈,这就意味着第一间房子和最后一间房子是挨着的。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。
给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。
分析
和上一个很类似。还是分别计算不抢劫第i所房子dp[i][0]和抢劫第i所房子dp[i][1]所能获得的最大钱数:
dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]);
dp[i][1] = dp[i - 1][0] + nums[i];
对于dp[A.length - 1][0],即不抢劫最后一所房子的计算方法一样;
唯一不同的是dp[A.length - 1][1],需要最先初始化dp[0][1] = 0
public int houseRobber2(int[] nums) {
if (nums == null || nums.length == 0)
return 0;
if (nums.length == 1)
return nums[0];
int[][] dp = new int[nums.length][2];
dp[0][1] = nums[0];
for (int i = 1; i < nums.length; ++i){
dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]);
dp[i][1] = dp[i - 1][0] + nums[i];
}
dp[0][1] = 0;
for(int i = 1; i < nums.length; ++i){
dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]);
dp[i][1] = dp[i - 1][0] + nums[i];
}
return Math.max(dp[nums.length-1][1], dp[nums.length - 1][0]);
}
III题目:
在上次打劫完一条街道之后和一圈房屋之后,窃贼又发现了一个新的可以打劫的地方,但这次所有的房子组成的区域比较奇怪,聪明的窃贼考察地形之后,发现这次的地形是一颗二叉树。与前两次偷窃相似的是每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且当相邻的两个房子同一天被打劫时,该系统会自动报警。
算一算,如果今晚去打劫,你最多可以得到多少钱,当然在不触动报警装置的情况下。
样例
3
/ \
2 3
\ \
3 1
窃贼最多能偷窃的金钱数是 3 + 3 + 1 = 7.
3
/ \
4 5
/ \ \
1 3 1
窃贼最多能偷窃的金钱数是 4 + 5 = 9.
解题思路:递归。
对当前节点为根节点的树进行讨论,有打劫和不打劫当前节点两种可能。如果打劫,则不能打劫其子节点;否则可以打劫其子节点。则其能获得的最大金钱为打劫和不打劫当前节点两种可能中的最大值。
如果仅是单纯的递归,则会有大量重复计算,导致超时。所以我使用Map保存已经计算的结果来避免重复计算
Map<TreeNode, Integer> map = new HashMap<TreeNode, Integer>();
public int houseRobber3(TreeNode root) {
if (root == null)
return 0;
if (map.containsKey(root))
return map.get(root);
int rob = root.val;
if (root.left != null) {
rob += houseRobber3(root.left.left);
rob += houseRobber3(root.left.right);
}
if (root.right != null) {
rob += houseRobber3(root.right.left);
rob += houseRobber3(root.right.right);
}
int noRob = houseRobber3(root.left) + houseRobber3(root.right);
m.put(root, Math.max(rob, noRob));
return Math.max(rob, noRob);
}