打家劫舍2

原题地址
最开始直接枚举起点

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.empty()) {
            return 0;
        }
        int size = nums.size();
        if(size == 1) {
            return nums.back();
        }

        vector<int> dp(size*2);//dp(i) 前i家不包括第i家 可偷到的最大值 所以前0家必然是0 前1家也是0 所以最后拿的是dp(i+size)进行的比较
        int maxN = 0;
        for(int i = 0; i < size; ++i) {
            for(int j = 0; j < size*2; ++j) {
                dp[j] = 0;
            }
            for(int k  = 2; k <= size; ++k) {
                dp[i+k] = max(dp[i+k-1], dp[i+k-2] + nums[(i+k-1)%size]);
            }
            maxN = max(maxN,dp[i+size]);
        }
        return maxN;
    }
};

但是想了想,这题和石子合并还是不一样的。
石子合并需要考虑那么多次情况,但这题受限于题目的要求,偷了一家不能再偷相邻的一家,重复计算了不少内容。
实质上只需要从0和1分别开始枚举长度就行了。

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.empty()) {
            return 0;
        }
        int size = nums.size();
        if(size == 1) {
            return nums.back();
        }

        vector<int> dp(size*2);
        int maxN = 0;
        for(int i = 0; i < 2; ++i) {
            for(int j = 0; j < size*2; ++j) {
                dp[j] = 0;
            }
            for(int k  = 2; k <= size; ++k) {
                dp[i+k] = max(dp[i+k-1], dp[i+k-2] + nums[(i+k-1)%size]);
            }
            maxN = max(maxN,dp[i+size]);
        }
        return maxN;
    }
};

又想起了可以用滚动数组优化一下空间。
顺带改善一下代码结构,使之更通用。

class Solution {
public:
    int rob(vector<int>& nums) {
        //边界情况
        if(nums.empty()) {
            return 0;
        }
        int size = nums.size();
        if(size == 1) {
            return nums.back();
        }
        if(size == 2) {//防止越界
            return max(nums[0], nums[1]);
        }
        //和之前的打家劫舍思路一致 和石头合并问题 的循环解法也很一致 我最开始放弃思考了 写了个o(n2)
        //但是仔细一想 其实首尾相接 和之前的打家劫舍1的 就差了一个元素
        //假如我偷了第一家 那么就不能偷最后一家 如果我偷了最后一家 那么我就不能再偷第一家了
        //从这个角度来看这个问题 当前问题就像是之前的缩小版本 但是需要分类讨论
        //按照这种思路 最后返回的值应当是下面这种
        //return max(robRange(nums, 2, size - 2) + nums[0], robRange(nums, 1, size - 3) + nums[size - 1]);
        //但是如果这样写了 就是默认最后结果必然包含第一个 或者 最后一个  如2 3 2这个测试样例
        //忽视掉了一些情况 
        /*以下为总情况
        偷了第一个 没有偷最后一个
        偷了最后一个 没有偷第一个
        没有偷第一个 没有偷最后一个
        两个变量总共4种情况
        其中不可能出现 偷了第一个 偷了最后一个
        要包含所有的情况 即 a = 0 || b = 0
        没有偷第一个 那么最后的结果就是 在[1,size-1]中 可以偷到的最大值 因为没有偷第一个嘛
        没有偷最后一个 那么最后的结果就是在 [0,size-2]中 可以偷到的最大值 因为没有偷到最后一个嘛
        再从这两个里面选一个最大的作为结果即可
        */
        return max(robRange(nums, 0, size - 2), robRange(nums, 1, size - 1));
    }
    // 偷窃指定数组中 真包含start, end下标的 元素 可达到的最大的值
    int robRange(vector<int>& nums, int start, int end) {
        int first = nums[start], second = max(nums[start], nums[start + 1]);
        for(int i = start + 2; i <= end; ++i) {
            int tmp = second;
            second = max(second, first + nums[i]);
            first = tmp;
            //cout << first << ' ' << second << endl;
        }
        return second;
    }
};

dp专题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值