House Robber II - LeetCode 213

题目描述:
Note: This is an extension of House Robber.

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Credits:
Special thanks to @Freezen for adding this problem and creating all test cases.

Hide Tags Dynamic Programming

分析:
该题是前面198 House Robber(见http://blog.csdn.net/bu_min/article/details/45252111)的升级版。上一题的所有房子是线性排列的,而本题的房子是首尾相连围成一个圈,所有房子的报警器和之前的设置是一样的,如果相邻房子被抢就会发生报警。本题同样是要求出在不发生报警的前提下,小偷最多能抢多少钱。

本题主要的难点是如果保证不同时抢首尾的房子。
首先,在前一题的基础上,从左往右遍历,得到了动态数组dp,记录了从左往右到每个房子时所能获得的最大值。那怎么判断首尾房子是否被抢呢?
 若dp[n-2]和dp[n-1]相等,则表明最后一个房子没有抢,此时不需要判断第一个房子是否被抢,直接返回dp[n-2]即可;
 若dp[n-2]和dp[n-1]不相等,则表明确定了最后一个房子是要抢的,但是不能抢第0个房子,那么怎么才能做到不抢第0个房子呢?我们可以再设置一个动态数组dpr,记录从右到左到每个房子时所能获得的最大值,但是不要抢第0个房子。最后返回dpr[1]和dp[n-2]中的最大值即可(dpr[1]表示不抢第0个房子,dp[n-2]表示不抢最后一个房子)。
方法一的思想可简单归纳为:两种抢劫方案:抢第0个房子,不抢最后一个;抢最后一个,不抢第0个。返回两种方案的最大值即可。

好了,逻辑有点绕,我都被自己讲晕了,好好整理下思路,AC了,不过貌似Leetcode的计时器是不是出问题了?这已经不只是第一次遇到0ms了

以下是C++实现代码:

/*///方法1/0ms*/
class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.empty())
            return 0;
        int n = nums.size();
        if(n == 1)   //处理只有一个房子的情况,只有一个可以抢,返回第0个房子的钱
            return nums[0];
        if(n == 2)
            return (nums[0] > nums[1])? nums[0]:nums[1]; //若只有两个元素,则由于相邻的房子不能抢,所以返回较大值
    
        /** 以下是大于2个元素的情况*/
        vector<int> dp;
        dp.push_back(nums[0]); //初始化dp[0]
        dp.push_back(dp[0] > nums[1] ? dp[0]:nums[1]); //初始化dp[1]


        for(int i = 2; i != n; i++) //从下标2开始,根据递推公式计算最大值
        {
            int k = dp[i-2] + nums[i];
            dp.push_back(dp[i-1] > k ? dp[i-1] : k);
        }
        if(dp[n-1] == dp[n-2]) //到最后一个元素和到倒数第二个元素的最大值是一样的,表明不抢最后一个房子,直接返回当前最大值即可
            return dp[n-2];
        else //最后一个房子需要抢,那么反向遍历,计算到每个元素的最大值,当然不能抢第0个房子。
        {
            vector<int> dpr(n,0);
            dpr[n-1] = nums[n-1]; //逆向初始化
            dpr[n-2] = dpr[n-1] > nums[n-2]? dpr[n-1]:nums[n-2];
            
            for(int i = n-3; i != 0; i--) //逆向计算动态最大值
            {
                int k = dpr[i+2] + nums[i];
                dpr[i] = dpr[i+1] > k? dpr[i+1]:k;
            }
            return dpr[1] > dp[n-2] ? dpr[1]:dp[n-2];  //返回不抢第0个房子和不抢最后一个房子的最大值,即为所求
        }
    }
};


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值