欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

力扣题目-----打家劫舍三道

程序员文章站 2022-03-24 14:08:34
...

例题一:

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

力扣题目-----打家劫舍三道

这道题直接简化为:给你一组数字,让你选择若干个数字,前提条件是你选的数字不能是相邻的。

思路:采用动态规划来做,对于第 k (k>2) 间房屋,有两个选项:

  1. 偷窃第 k 间房屋,那么就不能偷窃第 k-1间房屋,偷窃总金额为前 k−2 间房屋的最高总金额与第 kk 间房屋的金额之和。

  2. 不偷窃第 k 间房屋,偷窃总金额为前 k-1 间房屋的最高总金额。

在两个选项中选择偷窃总金额较大的选项,该选项对应的偷窃总金额即为前 k 间房屋能偷窃到的最高总金额。

用 dp[i] 表示前 i 间房屋能偷窃到的最高总金额,那么就有如下的状态转移方程:
力扣题目-----打家劫舍三道

//这里还可以用滚动数组来优化空间复杂度,自己改一改
class Solution {
    public int rob(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        int length = nums.length;
        if (length == 1) {
            return nums[0];
        }
        int[] dp = new int[length];
        dp[0] = nums[0];
        dp[1] = Math.max(nums[0], nums[1]);
        for (int i = 2; i < length; i++) {
            dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[length - 1];
    }
}

例题二:

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

力扣题目-----打家劫舍三道

思路

唯一的区别是此题中的房间是环状排列的(即首尾相接),而.上道题中的房间是单排排列的;而这也是此题的难点。

环状排列意味着第一个房子和最后一个房子中只能选择一个偷窃,因此可以把此环状排列房间问题约化为两个单排排列房间子问题:

  1. 在不偷窃第一个房子的情况下(即 nums[1]~nums[end]),最大金额是 p_1
  2. 在不偷窃最后一个房子的情况下(即 nums[0]~nums[end-1]),最大金额是 p_2

综合偷窃最大金额: 为以上两种情况的较大值,即 max(p1,p2)

class Solution {
public:
/*
环状排列意味着第一个房子和最后一个房子中只能选择一个偷窃,
因此可以把此环状排列房间问题约化为两个单排排列房间子问题:
在不偷窃第一个房子的情况下(即 nums[1:]),最大金额是p1;
在不偷窃最后一个房子的情况下(即 nums[:n-1]),最大金额是p2。
综合偷窃最大金额: 为以上两种情况的较大值,即 max(p1,p2)。
*/
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(n==0) return 0;
        if(n == 1) return nums[0];
        int Max1 = rob1(nums,0,n-1);//不偷窃最后一个房子
        int Max2 = rob1(nums,1,n);//不偷窃第一个房子
        return max(Max1,Max2);
    }
    
    //这里用到了滚动数组的优化
    int rob1(vector<int>& nums,int start,int end){
        /*
        动态转移方程是:
        f(n)=max(nums[n]+f(n-2),f(n-1))
        prevMax:f(k-2)
        currMax:f(k-1)
        x:Ak
        */
        int prevMax = 0;
        int currMax = 0;
        for(int i=start;i<end;i++){
            int temp = currMax;
            currMax = max(nums[i]+prevMax, currMax);
            prevMax = temp;
        }
        return currMax;
    }
};

例题三:

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

力扣题目-----打家劫舍三道
思路:

这道题可以分两种情况

  1. 偷父节点,如果偷了父节点的话,就不能偷子节点了,那也就是只能偷当前节点的所有孙子树的和。
  2. 不偷父节点,求偷两个子树的最高金额的和。

本质上,就是用二叉树的后序遍历来解决问题

//0代表不偷,1代表偷
class Solution {
public:
    int rob(TreeNode* root) {
        int *q = dfs(root);
        return max(q[0],q[1]);
    }

    int* dfs(TreeNode* root){
        if(root==NULL) return new int[2]();
        int *res = new int[2]();

        int *left = dfs(root->left);
        int *right = dfs(root->right);

        res[0] = max(left[0],left[1]) + max(right[0],right[1]);
        res[1] = left[0] + right[0] + root->val;

        return res;
    }
};