• 代码随想录笔记_动态规划_213打家劫舍II


    代码随想录笔记_动态规划

    代码随想录二刷笔记记录

    LC213.打家劫舍II


    题目

    单序列问题

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

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

    示例 1:

    输入:nums = [2,3,2]
    输出:3
    解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

    示例 2:

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

    示例 3:

    输入:nums = [1,2,3]
    输出:3


    思路分析

    思路
    由题可知,首尾相连,本题需要考虑环形数组的问题。根据题意可知,有两种情况

    • 取首部,不考虑数组的最后一个元素
    • 取尾部,不考虑数组的第一个元素

    以 [1,2,3,1,2] 为例

    s:start, e: end
    //不考虑尾部
    [1,2,3,1,2]
     |     |
     s     e
     
     //不考虑首部
    [1,2,3,1,2]
       |     |
       s     e 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    进一步分析,本题与 LC198 不同之处在于,本题存在首尾相连的两种情况,因此,我们将两种情况拆分开来,就相当于将LC198封装为一个函数,求出情况1的答案res1,求出情况2的答案res2,二者取最大值返回即可。

    动态规划五部曲

    1.确定dp数组及其下标的含义

    dp[i]:表示从第 0 家到第 i 间屋子,小偷能偷盗的最大金额
    
    • 1

    2.确定递推公式

    dp[i] 表示小偷从 0 到 i 间屋子所能偷盗的最大金额

    dp[n-1] : 一共有n 个屋子,dp[n-1]则表示小偷从所有屋子中能偷盗的最大金额。

    由思路分析可知,小偷在偷盗第 2 间屋子的时候,有两个条件

    • 不能偷盗第 1 间屋子
    • 已经偷到了第 0 间屋子的金额

    推广开来则为

    小偷偷第 i 间屋子时

    • 不能偷第 i-1 间屋子
    • 已经偷了第 i-2 间屋子

    因此可知

    dp[i] = dp[i-1] , dp[i-2] + nums[i] //从中取最大值
    dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]);
    
    • 1
    • 2

    3.初始化

    如果只有一间屋子,则 dp[0] = nums[0];
    如果有两间屋子,则 dp[1] = Math.max(nums[0],nums[1]);
    以情况1为例

    dp[0] = nums[0];
    dp[1] = Math.max(nums[0],nums[1]);
    
    • 1
    • 2

    以情况2为例

    dp[1] = nums[1];
    dp[2] = Math.max(nums[2],nums[3]);
    
    
    • 1
    • 2
    • 3

    因此

    int[] dp = new int[nums.length];
    
    • 1

    4.遍历顺序

    由递推公式可知:

    //从前向后遍历

    for(int i = start;i <= end;i++){
    	dp[start] = Math.max(dp[i-1],dp[i-2] + nums[i])
    }
    
    • 1
    • 2
    • 3

    代码实现

    完整代码实现

    public int rob(int[] nums) {
            if (nums == null || nums.length == 0) return 0;
            if (nums.length == 1) return nums[0];
            //case1:不考虑尾
            int res1 = robCircle(nums, 0, nums.length - 2);
            //case2:不考虑首
            int res2 = robCircle(nums,1,nums.length-1);
            return Math.max(res1,res2);
        }
         public int robCircle(int[] nums,int start,int end){
            if (start == end) return nums[start];
            //初始化
            int[] dp = new int[nums.length];
            dp[start] = nums[start];
            dp[start+1] = Math.max(nums[start],nums[start+1]);
            //遍历
            for (int i = start + 2; i <= end; i++) {
                dp[i] = Math.max(dp[i-1],dp[i-2] + nums[i]);
            }
            return dp[end];
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

  • 相关阅读:
    一个四位数,恰好等于去掉它的首位数字之后所剩的三位数的3倍,这个四位数是多少?
    机器学习+CFD的后续
    安全监控系统设备、设施管理制度
    Google Colab 快速上手
    【慕课-湖南大学】服务设计与商业模式
    接口防止重复提交,订单避免重复下单
    Shell 常用操作指令
    paddle 1-高级
    1043 输出PATest
    UEC++ 代理/委托
  • 原文地址:https://blog.csdn.net/Erik_Ying/article/details/126240763