动态规划【打家劫舍】

今天和大家分享一下动态规划当中的打家劫舍题目,希望在大家刷题的时候提供一些思路

打家劫舍1:

题目链接:  198. 打家劫舍 - 力扣(LeetCode)

题目描述:

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

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

示例 1:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。 

动规5部曲:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组

dp数组的含义:

当前下标所对应的下标所对应的最大可以偷窃的金钱

确定递推公式:

根据dp数组的含义可得,我们当前的金额最大值就是上一个房间可以偷窃的最大值和当前数值和上上一个房间可以偷窃的最大数值之和

dp数组如何初始化:

考虑最简单的情况,假如只有一间房间,当前的dp[0]就是num[0],如果只有两间房,那么当前的dp[0]为num[0],dp[1]为max(num[0],num[1])

确认遍历顺序:

只遍历房间

举例推导dp数组:

房子编号:     1    2    3    4    5
金额 (nums):   2    7    9    3    1

动态规划过程 (dp):
dp[0] = 2             (偷第一个房子)
dp[1] = max(2, 7) = 7 (偷第二个房子)

dp[2] = max(7, 2+9) = 11
dp[3] = max(11, 7+3) = 11
dp[4] = max(11, 11+1) = 12

最终最大金额: dp[4] = 12

假设数组为num,那么dp[0]为num[0],dp[1]为max(num[0],num[1]),通过遍历我们的房屋,那么就可以得到dp[i]=max(dp[i-1],dp[i]+dp[i-2])

思维讲解:

 详细了解dp数组的含义,我们这里每次dp取到的都是当前房间和前面房间可以偷窃的金钱最大数,所以当前dp值就是上一个dp或者num[i]+dp[i-2] ,从而通过遍历房间得到我们的dp

代码:

int rob(vector<int>& nums) {
        vector<int> dp(nums.size() + 1, 0);
        if(nums.size()<2) return nums[0];
        dp[0] = nums[0];
        dp[1] = max(nums[0],nums[1]);
        for (int i = 2; i < nums.size(); i++) {
            dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);
        }
        return dp[nums.size()-1];
    }
打家劫舍2:

题目链接: 213. 打家劫舍 II - 力扣(LeetCode)

题目描述:

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

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

示例 2:

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

 这个题目比前面的那个多了一个条件,就是房屋是一个环形的房间,那么这个题目怎么解决呢,如何进行初始化呢,选上第一个就不能选上最后一个,如何解决这个问题呢

因为这是一个环形房间,从哪里开始都一样,我们可以将房间分为两组,第一组就是从第一个房间到倒数第二个房间,然后第二组就是第二个房间到倒数第一个房间,然后得出这两组所求的最大金额,返回的就是两组当中最大金额的最大值,第几个到第几个的房间偷取的最大金钱数和打家劫舍1情况相同

为什么要这样想呢??? 

因为这里有两种情况:

1. 不偷最后一个房子

假设我们从第一个房子开始偷,这样就不需要考虑偷最后一个房子,因为它与第一个房子相邻。因此,问题就简化为一个普通的“打家劫舍”问题,即我们从第 1 个房子到第 n-1 个房子之间选择哪些房子偷。这个问题可以通过动态规划来解决。

2. 不偷第一个房子

假设我们从第二个房子开始偷,这样就不需要考虑偷第一个房子,因为它与第二个房子相邻。因此,这样的问题就变成了从第 2 个房子到第 n 个房子之间选择偷哪些房子。同样可以通过动态规划来解决。

 

 

最后通过比较11打于10所以选11,所能偷窃的最大金额为11 

代码:

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size() == 0)
            return 0;
        if (nums.size() == 1)
            return nums[0];
        if (nums.size() == 2)
            return max(nums[0], nums[1]);
        vector<int> dp1(nums.size(), 0);
        vector<int> dp2(nums.size(), 0);

        dp1[0] = nums[0];
        dp1[1] = max(nums[0], nums[1]);
        dp2[1] = nums[1];
        dp2[2] = max(nums[1], nums[2]);

        for (int i = 2; i < nums.size() - 1; i++) {
            dp1[i] = max(dp1[i - 2] + nums[i], dp1[i - 1]);
        }
        for (int i = 3; i < nums.size(); i++) {
            dp2[i] = max(dp2[i - 2] + nums[i], dp2[i - 1]);
        }
        return max(dp1[nums.size() - 2], dp2[nums.size() - 1]);
    }
};

 后面会继续补充的,感谢观看

<think>好的,我现在需要帮助用户查找关于Java中解决'打家劫舍'问题的动态规划算法实现。首先,我应该回顾用户提供的引用内容,看看是否有相关的信息。引用[1]提到了打家劫舍动态规划实现,引用[2]解释了动态规划的一般步骤,引用[3]则有一个Java的动态规划示例,虽然主题是爬楼梯和股票买卖,但可能结构上有参考价值。 用户的问题明确指向“打家劫舍”问题的动态规划解决方案,所以我需要从引用中找到最相关的部分。引用[1]的标题直接提到了这个问题,应该是最相关的。我需要查看该引用中的内容,看看是否有具体的Java代码实现。引用[1]中的内容提到了动态规划,但用户提供的引用内容里可能没有完整的代码,但可能有解题思路。 接下来,我应该根据动态规划的基本原理来构建解决方案。动态规划通常涉及定义状态、转移方程、初始条件和计算顺序。对于打家劫舍问题,每个房屋有两个状态:抢或不抢。因此,可以定义一个数组dp,其中dp[i]表示到第i个房屋时能获得的最大金额。或者,更优化的方式是用两个变量来保存前一个状态,节省空间。 用户提供的引用[3]中有一个动态规划的Java示例,虽然问题不同,但结构可以参考。例如,使用数组B来保存中间结果,循环处理每个状态。这可能帮助我构建打家劫舍的代码结构。 然后,我需要确保状态转移方程正确。打家劫舍的核心是不能连续抢劫相邻的房屋,所以对于第i个房屋,最大金额是max(抢当前房屋 + 前前房屋的最大金额, 不抢当前房屋即前一个房屋的最大金额)。这可以表示为dp[i] = max(dp[i-1], nums[i] + dp[i-2])。 在代码实现时,需要注意边界条件,比如数组为空或只有一个元素的情况。同时,可以进行空间优化,用两个变量代替数组,因为每次只需要前两个状态的值。 现在,我需要将这些思路转化为具体的Java代码。参考引用[1]中的可能结构和引用[3]中的动态规划示例,写出一个简洁高效的解决方案。同时,确保代码符合Java语法,比如数组声明、循环结构等。 最后,生成相关问题,可能涉及动态规划的其他应用、优化方法或者类似问题的变种,以帮助用户进一步学习。</think>### Java实现“打家劫舍动态规划算法 #### 问题描述 给定一个非负整数数组$nums$表示每个房屋存放的金额,不能同时抢劫相邻的房屋,求可获取的最大金额。 #### 分步解决方案 1. **定义状态** 设$dp[i]$表示前$i$个房屋能获得的最大金额,则有两种状态: - 不抢第$i$个房屋:$dp[i] = dp[i-1]$ - 抢第$i$个房屋:$dp[i] = dp[i-2] + nums[i]$ 2. **状态转移方程** $$dp[i] = \max(dp[i-1],\ dp[i-2] + nums[i])$$ 3. **空间优化** 由于每次只需前两个状态值,可将空间复杂度从$O(n)$优化到$O(1)$: ```java int prevMax = 0; // dp[i-2] int currMax = 0; // dp[i-1] ``` #### 完整代码实现 ```java public class Solution { public int rob(int[] nums) { int prevMax = 0; int currMax = 0; for (int num : nums) { int temp = currMax; currMax = Math.max(prevMax + num, currMax); prevMax = temp; } return currMax; } } ``` #### 关键点说明 - **初始化**:$prevMax$和$currMax$初始为0,对应空数组的情况[^1] - **循环逻辑**:遍历每个房屋时,计算当前最大金额并更新前两个状态 - **时间复杂度**:$O(n)$,空间复杂度$O(1)$[^2] #### 示例验证 输入$[2,7,9,3,1]$时: 1. 第1间:$\max(0+2,0)=2$ → $currMax=2$ 2. 第2间:$\max(0+7,2)=7$ → $currMax=7$ 3. 第3间:$\max(2+9,7)=11$ → $currMax=11$ 4. 第4间:$\max(7+3,11)=11$ → $currMax=11$ 5. 第5间:$\max(11+1,11)=12$ 最终输出$12$,符合选择第1、3、5间的最优解[^3]
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值