C++实现LeetCode(198.打家劫舍)

 更新时间:2021年08月06日 14:27:43   作者:Grandyang  
这篇文章主要介绍了C++实现LeetCode(198.打家劫舍),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

[LeetCode] 198. House Robber 打家劫舍

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

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.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.

Credits:
Special thanks to @ifanchu for adding this problem and creating all test cases. Also thanks to @ts for adding additional test cases.

这道题的本质相当于在一列数组中取出一个或多个不相邻数,使其和最大。那么对于这类求极值的问题首先考虑动态规划 Dynamic Programming 来解,维护一个一位数组 dp,其中 dp[i] 表示 [0, i] 区间可以抢夺的最大值,对当前i来说,有抢和不抢两种互斥的选择,不抢即为 dp[i-1](等价于去掉 nums[i] 只抢 [0, i-1] 区间最大值),抢即为 dp[i-2] + nums[i](等价于去掉 nums[i-1])。再举一个简单的例子来说明一下吧,比如说 nums为{3, 2, 1, 5},那么来看 dp 数组应该是什么样的,首先 dp[0]=3 没啥疑问,再看 dp[1] 是多少呢,由于3比2大,所以抢第一个房子的3,当前房子的2不抢,则dp[1]=3,那么再来看 dp[2],由于不能抢相邻的,所以可以用再前面的一个的 dp 值加上当前的房间值,和当前房间的前面一个 dp 值比较,取较大值当做当前 dp 值,这样就可以得到状态转移方程 dp[i] = max(num[i] + dp[i - 2], dp[i - 1]), 且需要初始化 dp[0] 和 dp[1],其中 dp[0] 即为 num[0],dp[1] 此时应该为 max(num[0], num[1]),代码如下:

解法一:

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size() <= 1) return nums.empty() ? 0 : nums[0];
        vector<int> dp = {nums[0], max(nums[0], nums[1])};
        for (int i = 2; i < nums.size(); ++i) {
            dp.push_back(max(nums[i] + dp[i - 2], dp[i - 1]));
        }
        return dp.back();
    }
};

还有一种解法,核心思想还是用 DP,分别维护两个变量 robEven 和 robOdd,顾名思义,robEven 就是要抢偶数位置的房子,robOdd 就是要抢奇数位置的房子。所以在遍历房子数组时,如果是偶数位置,那么 robEven 就要加上当前数字,然后和 robOdd 比较,取较大的来更新 robEven。这里就看出来了,robEven 组成的值并不是只由偶数位置的数字,只是当前要抢偶数位置而已。同理,当奇数位置时,robOdd 加上当前数字和 robEven 比较,取较大值来更新 robOdd,这种按奇偶分别来更新的方法,可以保证组成最大和的数字不相邻,最后别忘了在 robEven 和 robOdd 种取较大值返回,代码如下:

解法二:

class Solution {
public:
    int rob(vector<int>& nums) {
        int robEven = 0, robOdd = 0, n = nums.size();
        for (int i = 0; i < n; ++i) {
            if (i % 2 == 0) {
                robEven = max(robEven + nums[i], robOdd);
            } else {
                robOdd = max(robEven, robOdd + nums[i]);
            }
        }
        return max(robEven, robOdd);
    }
};

上述方法还可以进一步简洁,我们使用两个变量 rob 和 notRob,其中 rob 表示抢当前的房子,notRob 表示不抢当前的房子,那么在遍历的过程中,先用两个变量 preRob 和 preNotRob 来分别记录更新之前的值,由于 rob 是要抢当前的房子,那么前一个房子一定不能抢,所以使用 preNotRob 加上当前的数字赋给 rob,然后 notRob 表示不能抢当前的房子,那么之前的房子就可以抢也可以不抢,所以将 preRob 和 preNotRob 中的较大值赋给 notRob,参见代码如下:

解法三:

class Solution {
public:
    int rob(vector<int>& nums) {
        int rob = 0, notRob = 0, n = nums.size();
        for (int i = 0; i < n; ++i) {
            int preRob = rob, preNotRob = notRob;
            rob = preNotRob + nums[i];
            notRob = max(preRob, preNotRob);
        }
        return max(rob, notRob);
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/198

参考资料:

https://leetcode.com/problems/house-robber/description/

https://leetcode.com/problems/house-robber/discuss/55681/java-on-solution-space-o1

https://leetcode.com/problems/house-robber/discuss/55693/c-1ms-o1space-very-simple-solution

https://leetcode.com/problems/house-robber/discuss/55695/java-dp-solution-on-runtime-and-o1-space-with-inline-comment

到此这篇关于C++实现LeetCode(198.打家劫舍)的文章就介绍到这了,更多相关C++实现打家劫舍内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言中的二叉树和堆详解

    C语言中的二叉树和堆详解

    这篇文章主要介绍了C语言中的二叉树和堆详解,树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合,把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的,需要的朋友可以参考下
    2023-07-07
  • C语言三分钟精通时间复杂度与空间复杂度

    C语言三分钟精通时间复杂度与空间复杂度

    算法复杂度分为时间复杂度和空间复杂度。其作用: 时间复杂度是度量算法执行的时间长短;而空间复杂度是度量算法所需存储空间的大小
    2022-02-02
  • C++代码实现逆波兰式

    C++代码实现逆波兰式

    这篇文章主要为大家详细介绍了C++代码实现逆波兰式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • C语言小知识之为什么要使用指针详析

    C语言小知识之为什么要使用指针详析

    指针是C语言中一个非常重要的概念,也是C语言的特色之一,使用指针可以对复杂数据进行处理,能对计算机的内存分配进行控制,在函数调用中使用指针还可以返回多个值,这篇文章主要给大家介绍了C语言小知识之为什么要使用指针的相关资料,需要的朋友可以参考下
    2021-10-10
  • C语言运算符与表达式

    C语言运算符与表达式

    这篇文章主要介绍了C语言运算符与表达式,表达式是C语言的主体。在C语言中,表达式由操作符和操作数组成,更多相关介绍需要的小伙伴可以参考下面文章内容
    2022-07-07
  • C++ ASIO实现异步套接字管理详解

    C++ ASIO实现异步套接字管理详解

    Boost ASIO(Asynchronous I/O)是一个用于异步I/O操作的C++库,该框架提供了一种方便的方式来处理网络通信、多线程编程和异步操作,本文介绍了如何通过ASIO框架实现一个简单的异步网络套接字应用程序,需要的可以参考下
    2023-08-08
  • C语言简易扫雷游戏

    C语言简易扫雷游戏

    这篇文章主要为大家详细介绍了C语言简易扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • C++验证LeetCode包围区域的DFS方法

    C++验证LeetCode包围区域的DFS方法

    这篇文章主要介绍了C++验证LeetCode包围区域的DFS方法,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++实现俄罗斯方块小游戏

    C++实现俄罗斯方块小游戏

    这篇文章主要为大家详细介绍了C++实现俄罗斯方块小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • 纯C语言:贪心Prim算法生成树问题源码分享

    纯C语言:贪心Prim算法生成树问题源码分享

    这篇文章主要介绍了贪心Prim算法生成树问题源码,有需要的朋友可以参考一下
    2014-01-01

最新评论