一个小偷的自我修养


前言

    这周的leetcode,遇到了一个有意思的题目,名字叫打家劫舍。题目的意思是让你帮小偷规划偷窃的路线,是一道动态规划的题目。由于之前没有接触过动态规划算法,所以在这道题上花了很多的时间,借此文章总结一下。


一、题目描述

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

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

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

二、解题思路

1.第一个想法(失败的想法)

   一开始面对这个问题时,我的想法是这样的:
题目要求的是窃取到的总金额最多,那么我可以将所有房间的金钱都遍历一遍,每次偷取能够偷取的房间中金额最高的那间房,并且偷窃后将其标记为偷过的房间,之后循环这样的过程,直到没有房间可以偷。算法的步骤是这样的:
   step 1:遍历数组。
   step2:找到数组中金钱最大的那间房,然后判断是否能偷,即判断相邻的房间是否被偷过。
   step3:若能偷,则偷,将房间的钱计入偷到的金钱总数中,并且将其标记为偷过的房间;若不能偷则将其标记为不能偷的房间。
   step4:循环上述过程,直到所有房间都被标记完毕。
   step5:返回偷到的金钱总数。
   在仔细思考过后,我发现这个解法是有问题的。通过这个解法,你只能找出一个排序中数字最大的序列,但是能偷到的金额数不一定是最高的。假设属组中有几个重复的比较大的金额的房间时,就会出现问题。
   例如:输入[1,4,5,4,1]
   按照上述算法,其能够偷窃到的最高金额是6。但假设小偷先偷金额数为4的房间,其能够偷窃到的最高金额是8。可见第一个解法是有问题的。

 

2.动态规划(一种基于前面的状态决定当前状态的算法)

   基于遇到问题,自己没思路就要懂得抱大腿的方法论指引下,我去看了leetcode官方解答。官方解答应用了一种动态规划的算法。
   动态规划,我的理解就是走一步看一步,后边的状态由前面的一个或几个状态来决定。具体地解释我想引用一下我之前看的某篇博客:
六大算法之三:动态规划
   基于我们这个题目而言,我们可以由最简单的情况开始推理:
1.当没有房子时,小偷当然一分钱也偷不到,能偷到的金钱总数为0。
2.当只有一个房子时,小偷没有选择,能偷到的金钱总数就是当前房子所存的金钱数。
3.当有两个房子时,小偷可以选择偷或是不偷第二个房子。
   如果偷了第二个房子,就不能偷第一个房子,小偷能偷到的金钱总数就是第二个房子的金钱数加上第0个房子能偷到的最大的金钱总数,当然第0个房子不存在,所以金钱数为0,小偷能偷到的金钱总数就是第二个房子的金钱数。
   如果没偷第二个房子,小偷能偷到的金钱总数就是第一个房子能偷到的最大的金钱总数。
   最终小偷能偷到的最大的金钱数就是前面两种情况中,能偷到的金钱总数更大的那个。
4.这里可以归纳下,假设当前小偷已经偷到第i个房子(第i间房还没决定要不要偷),偷到的金钱数为dp[i-2],这时再加一间房子,小偷现在面临两个选择:
   1.偷第i间房子。此时小偷就不能偷第i-1间房子,此时,小偷能偷到的最大金钱总数为:
   dp[i]=dp[i-2]+nums[i];
   2.不偷第i间房子。此时,小偷能偷到的最大金钱总数为小偷偷前i-1间房子所能偷到的最大金钱数:
   dp[i]=dp[i-1];
   3.最终小偷能偷到的金钱总数为:上面两种选择中能偷到的金钱数更大的那个。

 

3.代码实现

   通过上文解释的动态规划算法,我们可以不用考虑全局,而是通过前一个或几个状态来决定当前的状态,直到走到结束的状态。对于本题的当前状态dp[i]是由dp[i-2]和nums[i],以及 dp[i-1]决定。所以,代码中我用first来存dp[i-2],用second来存dp[i-1]和dp[i-2]+nums[i]中较大的那一个。
 
C++代码实现如下:

class Solution {
public:
    int rob(vector<int>& nums) {
    int first,second;
    int length=nums.size();
    int temp;
    printf("length=%d\n",length);
    if(length==1){
        return nums[0];
    }
    if(length==2){
        return max(nums[0],nums[1]);
    }
    if(length>2){
        first=nums[0];
        second=max(nums[0],nums[1]);
        for(int i=2;i<length;i++){
            temp=second;
            second=max(first+nums[i],second);
            first=temp;
        }
        return second;

    }
    else {
        return 0;
    }

    }
};

 

总结

   这是我第一次接触动态规划算法,感觉动态规划算法,在解决规模为n的问题,并且当前状态由之前的一个或几个状态决定,同时之前的状态不由之后的状态影响的问题时,具有一定的优势。通过这次总结,希望自己在之后遇到类似的问题时,能够想起这个算法。本篇文章的代码和算法还有可以优化的部分,时间关系就没有优化了,之后有时间再看,溜了,溜了~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值