【动态规划】打劫家舍系列
第一题比较简单就不放了
213. 打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [0]
输出:0
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
来源:力扣(LeetCode)
开始感觉是不是用循环队列做,仔细想想不行,因为必须要全局最优。第一个和最后一个房间只能二选一或者全不选,一次遍历无法确定第一个和最后一个哪个更好。因此要遍历两次,分别保留第一个和最后一个房间,取更大的那个作为结果。
1 class Solution { 2 public int rob(int[] nums) { 3 int len=nums.length; 4 int yes=nums[0];//今天偷了 5 int no=0;//今天没偷 6 int preYes=yes; 7 int preNo=no; 8 for(int i=1;i<len-1;i++){//忽略最后 9 10 yes=preNo+nums[i];//今天要偷 11 no=Math.max(preYes,preNo);//今天不偷 12 preNo=no; 13 preYes=yes; 14 } 15 int res1=Math.max(yes,no); 16 17 yes=nums[len-1];//今天偷了 18 no=0;//今天没偷 19 preYes=yes; 20 preNo=no; 21 22 for(int i=len-2;i>0;i--){ 23 24 yes=preNo+nums[i];//今天要偷 25 no=Math.max(preYes,preNo);//今天不偷 26 preNo=no; 27 preYes=yes; 28 } 29 30 int res2=Math.max(yes,no); 31 return Math.max(res1,res2); 32 } 33 }
337. 打家劫舍 III
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
示例 2:
输入: [3,4,5,1,3,null,1]
3
/ \
4 5
/ \ \
1 3 1
输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.
来源:力扣(LeetCode)
只想到了最暴力地递归方法,693ms就离谱,只超过了25%的提交
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 12 public int rob(TreeNode root) { 13 14 return dfs(root); 15 16 17 18 } 19 20 public int dfs(TreeNode root){//如果偷当前节点,只能选择偷左右子树的子树了;如果不偷,那么就偷左右子树。比较这两者大小。 21 if(root==null)return 0; 22 if(root.left==null&&root.right==null)return root.val; 23 24 int ll=0; 25 int lr=0; 26 int rl=0; 27 int rr=0; 28 //四个子树的子树 29 30 if(root.left!=null){ 31 ll=dfs(root.left.left); 32 lr=dfs(root.left.right); 33 } 34 if(root.right!=null){ 35 rl=dfs(root.right.left); 36 rr=dfs(root.right.right); 37 } 38 39 return Math.max(ll+lr+rl+rr+root.val,dfs(root.left)+dfs(root.right)); 40 41 } 42 }
在评论区看到的一个大佬的思路,感觉看起来似乎和递归差不多,但是只用了1ms。两个代码的区别在于递归的分支数量。下面的只有两个分支,而上面的有六个分支,仔细体会。同时,使用数组定义状态,比用哈希表做缓存更加节省空间和时间。
1 class Solution { 2 3 public int rob(TreeNode root) { 4 5 //动态规划 6 //思路: 7 //定义一个数组res,长度为2,res[0]表示不抢该节点可获得最大值,res[1]表示抢劫该节点可获得最大值 8 //方法helper(r)意为:在以r为根节点的树中,返回抢劫根节点与不抢劫根节点可获得的最大值 9 //执行用时 2ms 10 int[] res = helper(root); 11 return Math.max(res[0],res[1]); 12 13 14 15 } 16 17 public int[] helper(TreeNode r){ 18 if(r == null) return new int[2];//边界条件,r为null时,跳出 19 int[] left = helper(r.left);//对于以r.left为根的树,计算抢劫根节点(r.left)与不抢劫根节点可获得最大金额. left[0]则为不抢r.lrft可获得的最大金额,left[1]则为抢劫r.left可获得的最大金额 以下right[] 分析同理 20 int[] right = helper(r.right); 21 int[] res = new int[2]; 22 res[0] = Math.max(left[0],left[1]) + Math.max(right[0],right[1]);//计算不抢劫当前根节点可获得的最大金额(那么其左右子树可以随便抢) 23 res[1] = r.val + left[0] + right[0];//计算若抢劫根节点可获得的最大金额(此时,其左右子树的根节点不能被抢) 24 return res; 25 } 26 }

浙公网安备 33010602011771号