将介绍如何使用动态规划解决三道打家劫舍问题:198.打家劫舍,213.打家劫舍II和337.打家劫舍III。
一、198.打家劫舍
题目描述:给定一个非负整数数组,代表每个房屋里的钱数,选择不相邻的房屋抢劫,求最大金额。
解题思路:使用动态规划,定义一个数组dp,dp[i]表示前i个房屋最大金额。则状态转移方程为:dp[i] = max(dp[i-1], dp[i-2]+nums[i]),即前i个房屋最大金额为前i-1个房屋最大金额或前i-2个房屋最大金额加上第i个房屋的钱数中的较大值。
Java代码如下:
public int rob(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
if (nums.length == 1) {
return nums[0];
}
int[] dp = new int[nums.length];
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]);
}
return dp[nums.length-1];
}
二、213.打家劫舍II
题目描述:给定一个非负整数数组,代表每个房屋里的钱数,首尾相连的房屋不能同时抢劫,求最大金额。
解题思路:由于首尾相连的房屋不能同时抢劫,因此第一个房屋和最后一个房屋不能同时被抢。可以将此问题转换为两个198.打家劫舍问题,一个是从第一个房屋开始抢劫,不抢最后一个房屋;另一个是从第二个房屋开始抢劫,可以抢最后一个房屋。最终答案为这两个问题的最大值。
Java代码如下:
public int rob(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
if (nums.length == 1) {
return nums[0];
}
return Math.max(rob(nums, 0, nums.length-2), rob(nums, 1, nums.length-1));
}
private int rob(int[] nums, int start, int end) {
int pre2 = 0, pre1 = 0;
for (int i = start; i <= end; i++) {
int cur = Math.max(pre1, pre2+nums[i]);
pre2 = pre1;
pre1 = cur;
}
return pre1;
}
三、337.打家劫舍III
题目描述:给定一个二叉树,每个节点代表一个房屋,节点内的值代表该房屋内的钱数。不能抢劫相邻的节点,求最大金额。
解题思路:在二叉树上使用动态规划,定义一个数组dp,dp[i][0]表示以第i个节点为根节点且不抢劫该节点所获得的最大金额,dp[i][1]表示以第i个节点为根节点且抢劫该节点所获得的最大金额。对于每一个节点i,其最大金额有两种情况:抢劫该节点,则不能抢劫其左右子节点,因此dp[i][1] = i.val + dp[i.left][0] + dp[i.right][0];不抢劫该节点,则可以抢劫其左右子节点,因此dp[i][0] = max(dp[i.left][0], dp[i.left][1]) + max(dp[i.right][0], dp[i.right][1])。最终答案为根节点的两种情况的最大值。
Java代码如下:
public int rob(TreeNode root) {
int[] res = robSub(root);
return Math.max(res[0], res[1]);
}
private int[] robSub(TreeNode root) {
if (root == null) {
return new int[2];
}
int[] left = robSub(root.left);
int[] right = robSub(root.right);
int[] res = new int[2];
// 抢劫该节点
res[1] = root.val + left[0] + right[0];
// 不抢劫该节点
res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
return res;
}
总结:以上三个问题都可以使用动态规划的思想解决。198.打家劫舍和213.打家劫舍II的状态转移方程比较简单,只需要考虑前一个状态和前两个状态的最大值即可。337.打家劫舍III在二叉树上使用动态规划,需要分别计算抢和不抢两种情况,最终答案取两种情况的最大值。