动态规划:打家劫舍

动态规划

打家劫舍

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

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

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

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

力扣上的题目,动态规划问题,简单记录一下我自己的做题过程(抄题的过程)

动态规划问题最关键的地方就是找到特殊值和状态转移方程。

首先给定一个数组,也就是房屋,如果只有一个房间,显然就直接返回就好了,如果有两个房间,那么就返回其中的最大值。代码如下:

if(len == 1)
	return nums[0];
if(len == 2)
	return max(nums[0],nums[1]);

状态转移方程

这里的状态转移方程需要好好理解,刚开始做动态规划的题目确实不是很会推的,简单描述下。
当进第一间房间的时候,只能取这一个 所以 S 0 = H 0 = 1 S_0 = H_0 = 1 S0=H0=1
当有两间房提供选择时,则是取两者最大值: S 1 = m a x ( S 0 , H 1 ) = 2 S_1 = max(S_0,H_1) = 2 S1=max(S0,H1)=2
当有三间房屋供选择时,由题意,如果选择不偷那么最大值还是 S 1 S_1 S1,如果偷了,则为之前的最大值+第三间屋子的值,即: S 2 = m a x ( S 2 , S 0 + H 2 ) = 4 S_2 = max(S_2,S_0+H_2) = 4 S2=max(S2,S0+H2)=4
当有四间屋子可以选择时,由题意,如果选择不透,那么最大值就是 S 2 S_2 S2,如果偷了就是之前的最大值+第四间房子的值,即: S 3 = m a x ( S 2 , S 1 + H 3 ) = 4 S_3 = max(S_2,S_1+H_3) = 4 S3=max(S2,S1+H3)=4

到这里递推公式就可以看出来了

S n = m a x ( S n − 1 , S n − 2 + H n ) S_n = max(S_{n-1},S_{n-2}+H_n) Sn=max(Sn1,Sn2+Hn)

到这里后就可以写代码了:

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

在力扣中,还有一个题目是这个题目的改进版本,这里也放出来,就是将这些房屋变成了一个环。

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

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

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。

那这个题目呢,其实和上面的是一样的,一个环就意味着第一个房子和最后一个房子不能同时取,那么可以将这个环分解成两个子问题,第一个子问题是从第一间房子到倒数第二间房子,第二个房子是从第二间到倒数第一间。

下面就是使用上面的方法,将这个问题分开求解返回最大值就好了。

代码如下:

    int rob(vector<int>& nums) {
        //从第一家开始,一直到倒数第二家
        //第第二家开始,一直到最后一家
        int len = nums.size();
        if(len == 0)
            return 0;
        else if(len == 1)
            return nums[0];
        else if(len == 2)
            return max(nums[0],nums[1]);
        else
        {
        vector<int>nums1(nums.begin(),nums.begin()+len-1);
        vector<int>nums2(nums.begin()+1,nums.begin()+len);
        int len1 = nums1.size();

        int dp1[len1];
        int max1;
        dp1[0] = nums1[0];
        dp1[1] = max(nums1[0],nums1[1]);
        for(int i=2;i<len1;i++)
            dp1[i] = max(dp1[i-1],dp1[i-2]+nums1[i]);
        max1 = dp1[len1-1];

        int dp2[len1];
        int max2;
        dp2[0] = nums2[0];
        dp2[1] = max(nums2[0],nums2[1]);
        for(int i=2;i<len1;i++)
            dp2[i] = max(dp2[i-1],dp2[i-2]+nums2[i]);
        max2 = dp2[len1-1];

        return max(max1,max2);
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值