总结分享 | 动态规划经典例题总结二

打家劫舍Ⅰ

题目

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

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

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

解题思路

一、定义数组元素的含义

那我们就定义一维数组dp[i] 的含义为:前 i+1 间房子能偷到的最大的金额。那么,dp[nums.length-1]就是我们要的答案了。

二、找出关系数组元素间的关系式

我们从子问题中来找数组元素之间的关系。

假设有房子 x1,x2,x3,可以看出我们只能选择(x2)或者(x1,x3),因此前三间房子能偷到的最大金额为dp[2]=max((x2),(x1+x3))。以此类推dp[3]=max(d[2],(dp[1]+x4))......

所以关系式为:

dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
三、找出初始值

可以看出,该关系式需要从 i=2 开始,否则数组索引会超出范围。因此我们需要定义dp[0],dp[1]的初始值。

另外还需要考虑nums数组的元素个数,

  • 如果nums数组为空,return 0;
  • 如果nums数组只有一个元素,return nums[0];
dp[0]=nums[0];
dp[1]=Math.max(nums[0],0+nums[1]);

代码

private static int rob(int[] nums){
    int n = nums.length;
    //判断数组nums的情况
    if (nums == null){
        return 0;
    } else if (n==1) {
        return nums[0];
    }
    //dp[i] 代表该下标i位置上 所能偷盗的最大金额
    int[] dp = new int[n]; 

	//定义初始值
    dp[0]=nums[0];
    dp[1]=Math.max(nums[0],0+nums[1]);
    
    // dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i])
    for (int i = 2; i < n; i++) {
        dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i]);
    }

    return dp[n-1];
}

打家劫舍Ⅱ

题目

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

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

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

解题思路

这道题和上一道的区别就在于房屋首位是相连的,偷了第一间就不能偷最后一间房屋。

两种情况:

  • 如果偷窃了第一间房屋,则不能偷窃最后一间房屋,因此偷窃房屋的范围是第一间房屋到最后第二间房屋;
  • 如果偷窃了最后一间房屋,则不能偷窃第一间房屋,因此偷窃房屋的范围是第二间房屋到最后一间房屋

代码

private static int rob(int[] nums){
    int n = nums.length;
    //判断数组nums的情况
    if (nums.length == 0){
        return 0;
    } else if (n==1) {
        return nums[0];
    }else if (n==2){
        return Math.max(nums[0],nums[1] );
    }

    return Math.max(robRange(nums,0,n-2),robRange(nums,1,n-1));
}
static int robRange(int[] nums,int start,int end ){
    
	//dp[i] 代表该下标i位置上 所能偷盗的最大金额
    int n = end-start+1;
    int[] dp = new int[n];
    
    //定义初始值
    dp[0]=nums[start];
    dp[1]=Math.max(nums[start],nums[start+1]);

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

    return dp[n-1];
}

代码优化:

private static int rob(int[] nums){
    int n = nums.length;
    //判断数组nums的情况
    if (nums.length == 0){
        return 0;
    } else if (n==1) {
        return nums[0];
    }else if (n==2){
        return Math.max(nums[0],nums[1] );
    }

    return Math.max(robRangePlus(nums,0,n-2),robRangePlus(nums,1,n-1));
}

private static int robRangePlus(int[] nums,int start,int end ){
    int a = nums[start];
    int b = Math.max(nums[start], nums[start + 1]);
    for (int i = start + 2; i <= end; i++) {
        int temp = b;
        b = Math.max(a + nums[i], b);
        a = temp;
    }
    return b;
}

posted @ 2022-12-22 09:57  Azureblue"  阅读(39)  评论(0编辑  收藏  举报