• 代码随想录笔记_动态规划_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

  • 相关阅读:
    Verilog中parameter在仿真时的应用
    Linux 中查看本机的子网掩码和网关
    (一)上市企业实施IPD成功案例分享之——方太
    Linux系统笔记Ⅰ
    flutter报错HTTP Host Availability (the doctor check crashed)的解决办法
    基于频谱的GCN的数学原理
    QT-事件循环机制
    ViewPager2+TabLayout
    TypeScript必知三部曲(一)TypeScript编译方案以及IDE对TS的类型检查
    clickhouse的安装和配置
  • 原文地址:https://blog.csdn.net/Erik_Ying/article/details/126240763