Leetcode 198. 打家劫舍【动态规划,一步步从最粗暴的动态规划到最优化滚动数组实现】

问题描述

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

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

解题报告

最简单粗暴的动态规划

创建一个二维数组 dp[size][2]
dp[i][1]表示偷窃第i个房屋得到的最高金额。
dp[i][0]表示不偷窃第i个房屋得到的最高金额。
所以转移方程为:
d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] + n u m s [ i ] dp[i][1]=dp[i-1][0]+nums[i] dp[i][1]=dp[i1][0]+nums[i]
d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] ) dp[i][0]=max(dp[i-1][0],dp[i-1][1]) dp[i][0]=max(dp[i1][0],dp[i1][1])

优化版动态规划

但实际上,我们不需要二维矩阵即可实现。
dp[i] 表示偷到第 i 个房屋所能得到的最高金额。当偷第 i 家房舍时,第 i-1 家房舍就不能偷,此时,dp[i]=dp[i-1]+nums[i] ;当不偷第 i 家房舍时,则是否偷窃第 i+1 家房舍不受影响,此时,dp[i]=dp[i-1] 。最终取两者的最大值即可。
转移方程为:
d p [ i ] = m a x ( d p [ i − 1 ] , d p [ i − 2 ] + n u m s [ i ] ) dp[i]=max(dp[i-1],dp[i-2]+nums[i]) dp[i]=max(dp[i1],dp[i2]+nums[i])

滚动数组版动态规划

在上一个方法的基础上,由于dp[i]只受到前两个状态的影响,所以我们可以使用大小为3的滚动数组来循环存储最新的三个值。

转移方程为: d p [ i % 3 ] = m a x ( d p [ ( i − 2 ) % 3 ] + n u m s [ i ] , d p [ ( i − 1 ) % 3 ] ) dp[i\%3]=max(dp[(i-2)\%3]+nums[i],dp[(i-1)\%3]) dp[i%3]=max(dp[(i2)%3]+nums[i],dp[(i1)%3])

在实现上,为了让循环从i=0开始,所以我们将转移方程改为:
d p [ ( i + 3 % 3 ] = m a x ( d p [ ( i − 2 + 3 ) % 3 ] + n u m s [ i ] , d p [ ( i − 1 + 3 ) % 3 ] ) dp[(i+3\%3]=max(dp[(i-2+3)\%3]+nums[i],dp[(i-1+3)\%3]) dp[(i+3%3]=max(dp[(i2+3)%3]+nums[i],dp[(i1+3)%3])
最后返回 d p [ ( n u m s . s i z e ( ) − 1 + 3 % 3 ] dp[(nums.size()-1+3\%3] dp[(nums.size()1+3%3] 即可。

实现代码

最简单粗暴的动态规划实现

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

优化版动态规划实现

int rob(vector<int>&nums){
    int n=nums.size();
    vector<int>dp(n);
    if(n==0) return 0;
    if(n==1) return nums[0];
    if(n==2) return max(nums[1], nums[0]);
    dp[0]=nums[0];
    dp[1]=max(nums[0],nums[1]);
    for(int i=2;i<n;i++){
        dp[i]=max(dp[i-1],dp[i-2]+nums[i]);
    }
    return dp[n-1];
}

滚动数组版动态规划实现

int rob(vector<int>&nums){
    vector<int>dp(3,0);
    for(int i=0;i<nums.size();i++){
        dp[(i+3)%3]=max(dp[(i+1)%3]+nums[i],dp[(i+2)%3]);
    }
    return dp[(nums.size()+2)%3];
}

总结

针对这种数组类 还是 不拿 的题目。我们不妨从最简单的思路开始,首先创建一个二维数组,第二维分别表示到第 i 个数, 产生的价值;不拿 产生的价值,接下来看是否有优化的空间。

参考资料

[1] Leetcode 198. 打家劫舍

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值