LintCode刷题——打劫房屋I、II、III

打劫房屋I:

题目内容:

假设你是一个专业的窃贼,准备沿着一条街打劫房屋。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。

样例:

给定 [3, 8, 4], 返回 8.

挑战:

O(n) 时间复杂度 且 O(1) 存储。

算法分析:

前提:对于某一间房子i,如果盗贼要打劫该房子,则房间对序号为i-1的房子(即前一所房子)盗贼不能进行打劫才能保证系统不报警。因此,很容易得出动态规划的表达式:

  ①建立一个数组DP[],DP[i]用来表示盗贼打劫到第i所房子时所能获得的最大金额数;

  ②根据前提的描述,盗贼不打劫当前房子,则DP[i] = DP[i-1];否则DP[i] = DP[i-2] + A[i];

因此DP[i] = max{ DP[i-1], DP[i-2] + A[i]};

代码:

public class Solution {
    /*
     * @param A: An array of non-negative integers
     * @return: The maximum amount of money you can rob tonight
     */
    /*时间开销为O(n) 空间开销为O(n);
    public long houseRobber(int[] A) {
        // write your code here
        if(A.length==0){
            return 0;
        }
        long[] DP = new long[A.length+1];
        DP[1] = A[0];
        for(int i=1;i<A.length;i++){
            DP[i+1] = Math.max(DP[i-1]+A[i],DP[i]);
        }
        return DP[A.length];
    }*/
    //空间开销为O(1);
    public long houseRobber(int[] A) {
        if(A.length==0){
            return 0;
        }
        if(A.length==1){
            return A[0];
        }
        long DP_i_1 = A[1];
        long DP_i_2 = A[0];
        for(int i=2;i<A.length;i++){
            long temp = DP_i_1;
            DP_i_1 = Math.max(temp,DP_i_2+A[i]);
            DP_i_2 = temp;
        }
        return DP_i_1;
    }
}

打劫房屋II:

题目内容:

在上次打劫完一条街道之后,窃贼又发现了一个新的可以打劫的地方,但这次所有的房子围成了一个圈,这就意味着第一间房子和最后一间房子是挨着的。每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且 当相邻的两个房子同一天被打劫时,该系统会自动报警。给定一个非负整数列表,表示每个房子中存放的钱, 算一算,如果今晚去打劫,你最多可以得到多少钱 在不触动报警装置的情况下。

注意事项:

这题是House Robber的扩展,只不过是由直线变成了圈。

样例:

给出nums = [3,6,4], 返回 6, 你不能打劫34所在的房间,因为它们围成一个圈,是相邻的。

算法分析:

本题在打劫房屋I的基础上增加了一个条件就是第一所房子与最后一所房子也不能兼得,其实本质并没有发生变化。在I中我们的建立的动态规划数组为DP[i]表示盗贼打劫到第i所房子获利的最大值,因此我们同样可以建立两个数组DP_1[] 和 DP_2[] 分别用来记录打劫区间为[1, n-1] 与 [2,n]的获利情况。这是因为第一所房子与最后一所房子不能够相邻,因此将其分为两个区间[1,n-1]与[2,n]后则就将问题化为了直线的打劫问题,也就可以运用I中的方法求解。

代码:

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
        if(nums.length==0||nums.length==2||nums==null){
            return 0;
        }
        if(nums.length==1){
            return nums[0];
        }
        int[] DP_1 = new int[nums.length-1];
        int[] DP_2 = new int[nums.length-1];
        //不打劫最后一所房子则从第一所房子开始打劫
        for(int i=0;i<nums.length-1;i++){
            if(i==0){
                DP_1[i] = nums[0];
            }
            if(i==1){
                DP_1[i] = Math.max(nums[1],DP_1[0]);
            }
            if(i>1){
                DP_1[i] = Math.max(DP_1[i-2]+nums[i],DP_1[i-1]);
            }
        }
        //打劫最后一所房子则从第二所房子开始打劫
        for(int i=1;i<nums.length;i++){
            if(i==1){
                DP_2[i-1] = nums[1];
            }
            if(i==2){
                DP_2[i-1] = Math.max(nums[2],DP_2[0]);
            }
            if(i>2){
                DP_2[i-1] = Math.max(DP_2[i-3]+nums[i],DP_2[i-2]);
            }
        }
        return DP_1[nums.length-2]>DP_2[nums.length-2]?DP_1[nums.length-2]:DP_2[nums.length-2];
    }
}

打劫房屋III:

题目描述:

在上次打劫完一条街道之后和一圈房屋之后,窃贼又发现了一个新的可以打劫的地方,但这次所有的房子组成的区域比较奇怪,聪明的窃贼考察地形之后,发现这次的地形是一颗二叉树。与前两次偷窃相似的是每个房子都存放着特定金额的钱。你面临的唯一约束条件是:相邻的房子装着相互联系的防盗系统,且当相邻的两个房子同一天被打劫时,该系统会自动报警。算一算,如果今晚去打劫,你最多可以得到多少钱,当然在不触动报警装置的情况下。

样例:

  3
 / 2   3
 \   \
  3   1

窃贼最多能偷窃的金钱数是 3 + 3 + 1 = 7.

    3
   /   4   5
 / \   \
1   3   1

窃贼最多能偷窃的金钱数是 4 + 5 = 9.

算法分析:

打劫房屋I、II均为对数组的动态规划处理,但本题是要求对二叉树进行一个动态规划处理。对于本题,首先要了解怎样是相邻:有直接连接的节点之间算相邻节点,即父节点与亲子节点;

对于二叉树中的某一个节点i,它也有偷与不偷这两个选项,若偷,则两个子节点不能偷;否则,两个子节点可以偷。与I、II不同的是,I、II中对当前的房屋i,偷与不偷仅需把其中的较大者保留下来进行,因为后续的结果均是建立在前者为最优解的基础上进行的,而且DP[i] = max{DP[i-1],DP[i-2]+A[i]}一定能保证不发生冲突;但是对于二叉树,当前节点i并不能那么方便的找到其子节点的子节点的最优解,因此并不能同I、II那样仅记录最优解。对于二叉树的节点而言,其最容易访问到的就是它的两个子节点。因此,可以建立一个大小为2的一维数组DP,其中DP[0]用来记录偷当前节点所能获利值,DP[1]用来记录不偷当前的值所能获利的值。因为二叉树由根节点开始进行发散,因此可以用后序遍历的方式最终返回一个二维数组。动态规划表达式如下:

  对于某一个节点node,若偷:DP[0] = left[1] + right[1] + node.val; 若不偷:DP[1] = max{left[0],left[1]} + max{right[0],right[1]};

代码:

/**
 * Definition of TreeNode:
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left, right;
 *     public TreeNode(int x) { val = x; }
 * }
 */

public class Solution {
    /*
     * @param root: The root of binary tree.
     * @return: The maximum amount of money you can rob tonight
     */
    public int[] postTrack(TreeNode node){
       if(node==null){
           int[] result = new int[]{0,0};
           return result;
       }
       int[] result = new int[2];
       int[] left = postTrack(node.left);
       int[] right = postTrack(node.right);
       //偷当前结点
       result[0] = left[1] + right[1] + node.val;
       //不偷当前节点
       result[1] = Math.max(left[0],left[1]) + Math.max(right[0],right[1]);
       return result;
    }
    public int houseRobber3(TreeNode root) {
        // write your code here
        int[] result = postTrack(root);
        return Math.max(result[0],result[1]);
    }
}

  

  

时间: 2024-11-19 16:19:09

LintCode刷题——打劫房屋I、II、III的相关文章

【leetcode刷题笔记】Permutations II

Given a collection of numbers that might contain duplicates, return all possible unique permutations. For example,[1,1,2] have the following unique permutations:[1,1,2], [1,2,1], and [2,1,1]. 题解:跟Permutation差不多,只是这次会有重复的元素,如下图所示,如果只用DFS的话就会产生重复的排列: 上

LintCode刷题笔记(九章ladder PartOne)--BugFree

九章ladder的前半部分刷题笔记,在这次二刷的时候补上~ @ 2017.05.21 141 - sqrtx 二分答案 ---  binarySearch二分法 --- class Solution: """ @param x: An integer @return: The sqrt of x """ def sqrt(self, x): # write your code here if not x: return 0 start, end

LintCode刷题——不同的二叉查找树I、II

不同的二叉查找树I: 题目内容: 给出 n,问由 1...n 为节点组成的不同的二叉查找树有多少种? 样例: 给出n = 3,有5种不同形态的二叉查找树: 1 3 3 2 1 \ / / / \ 3 2 1 1 3 2 / / \ 2 1 2 3 算法分析: 先来看一下二叉查找树的特点,当选定一个节点i作为中间节点时: ①位于该节点左子树中的所有节点均小于i,假设该节点的左子树的排列情况有m种: ②位于该节点右子树中的所有节点均大于i,假设该节点的右子树的排列情况有n种: ③综合①和②,当节点i

lintcode 中等题: 3 Sum II 三数之和II

题目 三数之和 II 给一个包含n个整数的数组S, 找到和与给定整数target最接近的三元组,返回这三个数的和. 样例 例如S = [-1, 2, 1, -4] and target = 1.  和最接近1的三元组是 -1 + 2 + 1 = 2. 注意 只需要返回三元组之和,无需返回三元组本身 解题 和上一题差不多,程序也只是稍微修改了 public class Solution { /** * @param numbers: Give an array numbers of n integ

lintcode 中等题:N Queens II N皇后问题 II

题目: N皇后问题 II 根据n皇后问题,现在返回n皇后不同的解决方案的数量而不是具体的放置布局. 样例 比如n=4,存在2种解决方案 解题: 和上一题差不多,这里只是求数量,这个题目定义全局变量,递归的时候才能保存结果,参考程序 java程序: class Solution { /** * Calculate the total number of distinct N-Queen solutions. * @param n: The number of queens. * @return:

LintCode刷题笔记-- LongestCommonSquence

题目描述: Given two strings, find the longest common subsequence (LCS). Your code should return the length of LCS. 解题思路: 这一题是非常经典的动态规划问题,在解题思路上可以按照经典的动态规划的解法,这是在系统学习动态规划之后第一个解决的LintCode上的问题: 1.子问题划分 给出两个字符串的A,B,两个字符串长度分别为lenA,lenB,求出两个字符串的LCS: 这划分为子问题:A.

LintCode刷题笔记-- Maximal Square

题目描述: Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area. Example For example, given the following matrix: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Return 4. 解题思路: 1.这一题明显使用动态规划来解题,开始想法使用动态

LintCode刷题笔记-- BackpackIV

动态规划 描述: Given an integer array nums with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target. Example Given nums = [1, 2, 4], target = 4 The possible combination ways are: [1, 1,

LintCode刷题笔记-- PaintHouse 1&amp;2

动态规划 题目描述: There are a row of n houses, each house can be painted with one of the k colors. The cost of painting each house with a certain color is different. You have to paint all the houses such that no two adjacent houses have the same color. The