打家劫舍(House Robber)Java动态规划入门分析二

打家劫舍动态规划解析
本文通过分析“打家劫舍”问题,探讨如何利用动态规划求解最优解。通过对不同情况的深入剖析,提炼出动态规划方程,并提供两种实现方案。

前言

我是想通过一些例题分析来带领我和大家入动态规划的门,若是我哪里分析或思考的有误区,或者错误,请大家能够对我进行评论或私信指正
1.使用最小花费爬楼梯(Min Cost Climbing Stairs) Java动态规划入门分析一
2.打家劫舍(House Robber)Java动态规划入门分析二
3.剪绳子(CutRope) Java动态规划入门分析三

题干

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋
装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,
能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12///= )

入门分析

这种简单的动态规划题目,首先要先思考动态规划方程,怎么分析动态规划方程(其实对于新手来说这就是个找规律的过程),我们不是大神, 一眼就看出。我们可以从个例分析到特例,我们可以先分析没有元素的情况,没有元素则f(0) = 0;然后分析元素为1的时候f(1) = nums[0],元素为2的时候,f(2) = Math.max(sums[0],sums[1]),当分析元素为3,这个时候需要作比较,nums[0]和nums[2]相加与nums[1]做对比,也就是f(3) = Math.max(sums[0] + sums[2],sums[1]),当分析元素为4的时候,f(4) = Math.max(sums[1] + sums[3],sums[0] + sums[2]),看到这里应该就有一些规律了吧,但是这真的对吗???,哈哈哈,题上并没有说间隔两个的不可以,比如a = [2,1,1,2],这个时候a[0] + a[2]与a[1] + a[3] = 3 但是正确的答案应该为4呀,也就是a[0]+a[3] = 4,这个时候就应该回去看看我们的分析有错误没,原来我们在f(3)的时候就分析错误了,f(3) =Math.max(f(1)+nums[2],f(2)),看到这里可能该有一丝疑惑,为什么要这样呢(为什么第一个与第二个元素要做对比)?
我们分析一下当数组为[2,1,1,2]的时候,假设只考虑间隔一个的正常情况下,0与2结合,1与3结合没问题,但是1元素能结合的,我0元素也能结合,他不能结合的2元素我也可以结合,此时1元素还小于0元素,我何不直接替代你1元素,这样就相当于考虑了0元素可以和间隔一个和间隔两个结合的情况,可能有些绕,多多担待,能力有限。
此时会发现我们找的是当前房屋金额和之前的间隔房屋最大金额与相邻房屋最大金额的最大值,得出动态规划方程:f(n) = Math.max(nums[i]+f(n-2),f(n-1));

代码

解法一

public static int rob(int[] nums) {
         if (nums.length == 1) {
             return nums[0];
         }
         if(nums.length == 2) {
             return Math.max(nums[0], nums[1]);
         }
         if(nums.length > 2) {
             int cost[] = new int[nums.length + 1];
             cost[0] = nums[0];
             cost[1] = Math.max(nums[0], nums[1]);
             for (int i = 2; i <= nums.length; ++i) {
                 int sum = i == nums.length ? 0 : nums[i];
                 cost[i] = Math.max(cost[i-2] + sum,cost[i-1]);
             }       
             return cost[nums.length];
         }
        return 0;
        }

解法二

        if (nums.length < 1 || nums == null) {
             return 0;
         }
        int length = nums.length;
        if (length == 1) {
             return nums[0];
         }
         if(length == 2) {
             return Math.max(nums[0], nums[1]);
         }
             int sum = 0;
             int sum1 = nums[0];
             int sum2 = Math.max(nums[0], nums[1]);
             int i = 2;
             while (i < length) {
                 sum = Math.max(sum1 + nums[i], sum2);
                 sum1 = sum2;
                 sum2 = sum;
                 i++;
             }
             return sum2;

若是哪里有理解错误的或写错的地方,望各位读者评论或者私信指正,不胜感激。
题目网址:https://leetcode-cn.com/problems/house-robber/description/

### 打家劫舍问题的动态规划Java代码 以下是针对“打家劫舍”问题的经典动态规划解决方案及其输出示例: #### 非环形排列的情况 在这种情况下,房屋并不形成闭环,因此只需按照线性顺序处理。 ```java public class HouseRobber { public static int rob(int[] nums) { if (nums.length == 0) return 0; if (nums.length == 1) return nums[0]; int prev1 = Math.max(nums[0], nums[1]); int prev2 = nums[0]; int temp = 0; for (int i = 2; i < nums.length; i++) { temp = Math.max(prev1, prev2 + nums[i]); prev2 = prev1; prev1 = temp; } return prev1; } public static void main(String[] args) { int[] houses = {1, 2, 3, 1}; System.out.println("最大可偷金额:" + rob(houses)); // 输出:4 } } ``` 此代码实现了非环形排列下的最优解算法[^4]。通过迭代更新`prev1`和`prev2`变量来存储当前状态的最大收益。 --- #### 环形排列的情况 当房屋形成闭环时,需分别考虑两种情况: 1. **不偷第一家**; 2. **不偷最后一家**。 最终取两者中的较大值作为结果。 ```java public class CircularHouseRobber { public static int rob(int[] nums) { if (nums.length == 0) return 0; if (nums.length == 1) return nums[0]; return Math.max( helper(Arrays.copyOfRange(nums, 0, nums.length - 1)), // 不偷最后一家 helper(Arrays.copyOfRange(nums, 1, nums.length)) // 不偷第一家 ); } private static int helper(int[] nums) { if (nums.length == 0) return 0; if (nums.length == 1) return nums[0]; int prev1 = Math.max(nums[0], nums[1]); int prev2 = nums[0]; int temp = 0; for (int i = 2; i < nums.length; i++) { temp = Math.max(prev1, prev2 + nums[i]); prev2 = prev1; prev1 = temp; } return prev1; } public static void main(String[] args) { int[] circularHouses = {2, 3, 2}; System.out.println("最大可偷金额(环形):" + rob(circularHouses)); // 输出:3 } } ``` 在此实现中,`helper`方法负责解决非环形排列的问题,而主函数则通过分治策略解决了环形排列带来的约束条件[^5]。 --- ### 输出示例解释 - 对于输入 `{1, 2, 3, 1}`,程序输出 `4`,表示可以选择偷第(`2`) 和第三家 (`3`) 来获得最大收益。 - 对于输入 `{2, 3, 2}`,由于房屋形成了闭环,程序输出 `3`,即仅选择偷第家以避免触发警报系统。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值