【LeetCode】198.打家劫舍

题目

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

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

题解

方法一:动态规划(递归实现)

算法

解这道题的思路采用动态规划的思想

设第 i i i个房屋的现金为 A i A_i Ai,有 n n n个房屋时能够偷窃到的最高金额为 f ( n ) f(n) f(n),则:

  1. 如果有一户人家,那答案就是本身,即 A 0 A_0 A0
  2. 如果有两户人家,那就是 m a x ( A 0 , A 1 ) max(A_0, A_1) max(A0,A1)
  3. 如果有三户人家,则为 m a x ( A 0 + A 2 , A 1 ) max(A_0+A_2, A_1) max(A0+A2,A1)

由此可以归纳出:
f ( n ) = m a x ( f ( n − 2 ) + A n , f ( n − 1 ) ) f(n)=max(f(n-2)+A_n,f(n-1)) f(n)=max(f(n2)+An,f(n1))

从上式可以看出符合动态规划的全局最优解蕴含在局部最优解的思想
实现算法的时候可以参照斐波那契数列的递归实现方法,也是最简单能想到的一个方法

代码

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size() == 0)
            return 0;
        return Rob(nums, nums.size() - 1);
    }
    int Rob(vector<int>& nums, int n) {
        if (n == 0)
            return nums[0];
        else if (n == 1)
            return nums[0] > nums[1] ? nums[0] : nums[1];
        int a = Rob(nums, n - 2) + nums[n];
        int b = Rob(nums, n - 1);
        return a > b ? a : b;
    }
};

分析

简单递归实现大量重复计算底层元素

  • 时间复杂度: O ( 2 n ) O(2^n) O(2n)
  • 空间复杂度: O ( n ) O(n) O(n),递归树的深度可以达到 n n n
  • LeetCode排名:超时

方法二:动态规划(数组实现)

算法

算法思路也是动态规划:
f ( n ) = m a x ( f ( n − 2 ) + A n , f ( n − 1 ) ) f(n)=max(f(n-2)+A_n,f(n-1)) f(n)=max(f(n2)+An,f(n1))

不过实现的时候没有使用递归,而是从前往后将每次的最优解都存在数组里

代码

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(n == 0)
            return 0;
        else if (n == 1)
            return nums[0];
        else if (n == 2)
            return nums[0] > nums[1] ? nums[0] : nums[1];
        
        int* dp = new int[n];
        dp[0] = nums[0];
        dp[1] = nums[0] > nums[1] ? nums[0] : nums[1];
        for (int i = 2; i < n; i++) {
            int a = dp[i - 2] + nums[i];
            int b = dp[i - 1];
            dp[i] = a > b ? a : b;
        }
        return dp[n - 1];
    }
};

分析

  • 时间复杂度: O ( n ) O(n) O(n),一次遍历即可
  • 空间复杂度: O ( n ) O(n) O(n),创建了n大小的空间
  • LeetCode排名: 4 m s 4ms 4ms

方法三:动态规划(常数空间)

算法

算法思路也是动态规划:
f ( n ) = m a x ( f ( n − 2 ) + A n , f ( n − 1 ) ) f(n)=max(f(n-2)+A_n,f(n-1)) f(n)=max(f(n2)+An,f(n1))

可以注意到,计算第 i i i个最优解 f ( i ) f(i) f(i)时,只需要 f ( i − 2 ) f(i-2) f(i2) f ( i − 1 ) f(i-1) f(i1),故可以对上面的算法进行改进
存储上摒弃了新开辟线性空间的做法,而是只用了两个变量来存储这两个最优解

代码

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size() == 0)
            return 0;
        
        int pre = 0, cur = 0;   //previous and current maximum result
        for (const auto& i : nums) {
            int t = cur;
            cur = cur > pre + i ? cur : pre + i;
            pre = t;
        }
        return cur;
    }
};

分析

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1),只用常数空间
  • LeetCode排名: 0 m s 0ms 0ms,有时跳到 4 m s 4ms 4ms
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值