LeetCode 打家劫舍系列问题 (练DP很好的例子)

    • 打家劫舍I(动态规划)
    • 打家劫舍II(动态规划)
    • 打家劫舍III(树型DP)

打家劫舍I(动态规划)

LeetCode 打家劫舍系列问题 (练DP很好的例子)_第1张图片

     状态表示:dp[i][0/1]表示第i个房子偷或不偷的最高金额
     初始边界:dp[0][] = {0,0}
     转移方程:
     当前房子不偷:dp[i][1] = dp[i-1][0] + nums[i]
     当前房子偷:  dp[i][0] = max{dp[i-1][0],dp[i-1][1]} 
    int rob(vector<int>& nums) {
        vector<vector<int>> dp(nums.size()+1,vector<int>(2,0));
        for(int i = 1; i <= nums.size(); i++)
        {
            dp[i][0] = max(dp[i-1][1],dp[i-1][0]);
            dp[i][1] = dp[i-1][0] + nums[i-1];
        }
        return max(dp[nums.size()][0],dp[nums.size()][1]);
    }

时间复杂度:O(N)
空间复杂度:O(N)

	优化:用四个变量表示代替上面的状态
    int rob(vector<int>& nums) {
        int n = nums.size();
        int last_0 = 0, last_1 = 0, curr_0 = 0, curr_1 = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            curr_0 = max(last_0, last_1);
            curr_1 = last_0 + nums[i];
            last_0 = curr_0;
            last_1 = curr_1;
        }
        return max(curr_0, curr_1);
    }

时间复杂度:O(N)
空间复杂度:O(1)

打家劫舍II(动态规划)

LeetCode 打家劫舍系列问题 (练DP很好的例子)_第2张图片
这道题目和第一道描述基本一样,强盗依然不能抢劫相邻的房子,输入依然是一个数组,但是告诉你这些房子不是一排,而是围成了一个圈。

也就是说,现在第一间房子和最后一间房子也相当于是相邻的,不能同时抢。比如说输入数组 nums=[2,3,2],算法返回的结果应该是 3 而不是 4,因为开头和结尾不能同时被抢。
那么就会有这两种情况:
第一间房子抢的化最后一间房子就不能抢
最后一间房子抢的话第一间房子就不能抢
所以结合打家劫舍I我们可以实现这样种情况,然后返回这两种情况的最大值就是答案。

    int rob(vector<int>& nums) {
        if(nums.size() == 0) return 0;
        if(nums.size() == 1) return nums[0];
        // 第一间房子抢的化最后一间房子就不能抢
        vector<int> nums1(nums.begin()+1,nums.end());
        // 最后一间房子抢的话第一间房子就不能抢
        vector<int> nums2(nums.begin(),nums.end()-1);
        // 取两者最大值
        return max(rob2(nums1),rob2(nums2));
    }
    int rob2(vector<int>& nums) {
        int pre = 0,curr = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            int tmp = curr;
            curr = max(curr,pre + nums[i]);
            pre = tmp;
        }
        return curr;
    }

打家劫舍III(树型DP)

LeetCode 打家劫舍系列问题 (练DP很好的例子)_第3张图片
状态转移方程:

	当前房子偷 = 左房子不偷 + 右房子不偷 + 当前房子的钱
	当前房子不偷 = max(左房子不偷,左房子偷) + max(右房子不偷,右房子偷)

函数maxPrifox:返回的是偷和不偷的最优策略对应的结果值

    struct data
    {
        int y; // 偷
        int n; // 不偷
    };
    int rob(TreeNode* root) {
        if(!root) return 0;
        data rs =  maxPrifox(root);
        return max(rs.y, rs.n);
    }
    data maxPrifox(TreeNode* root)
    {
        if(!root) return {0, 0};
        // 1. 拿到左房子的所有状态信息
        data le = maxPrifox(root->left);
        // 2. 拿到右房子的所有状态信息
        data ri = maxPrifox(root->right);
        data rs;
        // 3. 当前房子偷 = 左房子不偷 + 右房子不偷 + 当前房子的钱
        rs.y = le.n + ri.n + root->val;
        // 4. 当前房子不偷 = max(左房子不偷,左房子偷) + max(右房子不偷,右房子偷)
        rs.n = max(ri.y, ri.n) + max(le.y, le.n);
        return rs;
    }

你可能感兴趣的:(LeetCode)