【牛客 NC176】打家劫舍(一)

📚问题描述

原问题地址:打家劫舍(一)_牛客网 (nowcoder.com)

知识点:动态规划

难度:⭐⭐⭐

要求:

  • 时间限制:2秒
  • 空间限制:256M

描述

你是一个经验丰富的小偷,准备偷沿街的一排房间,每个房间都存有一定的现金,为了防止被发现,你不能偷相邻的两家,即,如果偷了第一家,就不能再偷第二家;如果偷了第二家,那么就不能偷第一家和第三家。

给定一个整数数组 nums,数组中的元素表示每个房间存有的现金数额,请你计算在不被发现的前提下最多的偷窃金额。

数据范围:数组长度满足 $ 1 ≤ n ≤ 2\times 10^5 $ ,数组中每个值满足 $ 1 ≤ num[i] ≤ 5000 $。

❌错误解题思路

当我看到这个题目的时候,考虑的非常简单,无非就是通过检查两家之间的金额差距,来决定最后的金额。

public class Solution {
    public int rob (int[] nums) {
        // 偶数就从第二个开始
        // 奇数就从第一个开始
        // 后来我看见第三个示例
        // 打破了我这种想法 终究是我太年轻了
        if(nums.length==1)return nums[0];
        // 这里通过相邻两家钱数差距来决定最后能偷取的最大钱数
        int num = 0;    // 差数
        long res = 0l;    // 总数
        int count = 1;  // 索引
        while(count<nums.length){
            res += nums[count-1];
            num += nums[count-1]-nums[count];
            count +=2;
        }
        if(nums.length%2==1){
            num += nums[count-1];
            res += nums[count-1];
        }
        res = num>0?res:res+Math.abs(num);
        return (int)res;
    }
}

10/14 组用例通过 运行时间265ms 占用内存30388KB 预期输出 293318815 实际输出 250223388

没错上面是无法通过所有示例的,因为没有考虑有某家的钱数高于相邻几家钱数的总和这种情况。

例如:[2, 3, 10, 5, 2, 9] 这种情况,那么你觉得如何才能让小偷偷到最多的钱数❓

按上面那种方式比较两家的钱数差距来决定最后偷取的总钱数 17 = 3 + 5 + 9,显然是这个答案是不正确的,实际上小偷可以偷取 21 = 2 + 10 + 9。

✔️动态规划解法

其实一开始我并没有看到知识点是动态规划,就直接去做了。

这也导致程序出现问题,不能通过测试。

public class Solution {
    public int rob (int[] nums) {
        int[][] dp = new int[2][2];
        dp[0][0] = 0;
        dp[0][1] = nums[0];
        for(int i = 1; i<nums.length;i++){
            // 当前这家小偷不偷 取上次偷或不偷中的金额最大的那个
            dp[1][0] = dp[0][0] > dp[0][1]?dp[0][0]:dp[0][1];
            // 当前这家小偷偷 只能取上次不偷的金额
            dp[1][1] = dp[0][0] + nums[i];
            // 我这里并没有保留小偷每经过一家偷与不偷的情况
            // 将当前偷与不偷定义为上一次偷与不偷的金额 为小偷到下一家偷与不偷做准备
            dp[0][0] = dp[1][0];
            dp[0][1] = dp[1][1];
        }
        return dp[0][0] > dp[0][1]?dp[0][0]:dp[0][1];
    }
}

通过百度百科关于动态规划的描述(当然偷看了一下其他人的写法😂),我只需要考虑当前状态偷与不偷的情况来与之前的数据进行相应计算。

从上面的代码来分析这一现象:

假设 i = 1,如果小偷当前偷的话,那么就加上上次小偷没有偷的情况;如果小偷当前不偷的话,那么就加上上次小偷偷的情况。

也就是当前小偷不偷的金额为 money = max(0, nums[0]),而小偷偷的金额为 money = 0 + nums[i]。

当 i = 2时,小偷不偷的金额为上次 i = 1小偷不偷与偷中最大的一个值,而小偷偷的金额为当前偷的这家的金额 + 上次 i = 1 小偷不偷的金额。

按照这种方式延续下去,直至小偷到最后一家,如果最后一家不偷那么比较前一次偷与不偷的金额来决定返回值;如果最后一家偷那么直接返回当前这家的金额 + 上次不偷的金额。

想必你已经关注到了,不管当前小偷来到哪一家,都需要去记录它偷这家或不偷这家的情况,偷取的金额并不是由上次的两种情况可以决定的,而是由当前的情况决定的。

为什么这样说,你想如果当前不偷,那么就是在上次两种情况里面选最大的金额;如果当前偷,那么就只能是上次不偷的情况的金额。

可能看得比较啰嗦,总结一句话就是动态规划不受之前的某种情况的影响,只受到当前情况的影响,因为之前的情况是一个已知的情况,而当前情况是需要进行计算或处理才能决定的。

📚动态规划

求解决策过程最优化的过程

将整个求解过程分成若干个求解过程,在每个求解过程中都需要做出抉择,从而使最终整个求解过程可以通过抉择获取最优过程。

这个就有点类似人生的选择,如果你有目标那么需要靠你自己去做出最优抉择。例如你想学 Java,可以选择自学,也可以选择培训,对于现在的你来说那种方式是最优的,那么你可以抉择哪种情况,只不过这些决定随着时间的逼近需要决定下来的。

当然如果你说你突然不想学了,那么在动态规划里面是不可以出现的,因为动态规划必须在一开始就制定好了一系列的规则或目标。



如果你是无意刷到这篇文章并看到这里,希望你给我的文章来一个赞赞👍👍。如果你不同意其中的内容或有什么问题都可以在下方评论区留下你的想法或疑惑,谢谢你的支持!!😀😀

参考资料

  1. 百度百科—动态规划
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hjhcos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值