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

打劫房屋I:node

題目內容:算法

假設你是一個專業的竊賊,準備沿着一條街打劫房屋。每一個房子都存放着特定金額的錢。你面臨的惟一約束條件是:相鄰的房子裝着相互聯繫的防盜系統,且 當相鄰的兩個房子同一天被打劫時,該系統會自動報警。給定一個非負整數列表,表示每一個房子中存放的錢, 算一算,若是今晚去打劫,你最多能夠獲得多少錢 在不觸動報警裝置的狀況下。數組

樣例:post

給定 [3, 8, 4], 返回 8.spa

挑戰:code

O(n) 時間複雜度 且 O(1) 存儲。blog

算法分析:it

前提:對於某一間房子i,若是盜賊要打劫該房子,則房間對序號爲i-1的房子(即前一所房子)盜賊不能進行打劫才能保證系統不報警。所以,很容易得出動態規劃的表達式:io

  ①創建一個數組DP[],DP[i]用來表示盜賊打劫到第i所房子時所能得到的最大金額數;class

  ②根據前提的描述,盜賊不打劫當前房子,則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]);
    }
}
相關文章
相關標籤/搜索