LeetCode 198 House Robber

解决一个专业窃贼如何在不触动相邻房屋警报系统的情况下,从一系列房屋中窃取最大金额的问题。采用动态规划的方法,通过递归、记忆化递归及最终优化后的动态规划算法来求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.


动态规划

状态
  • 递归函数的定义就是动态规划中的状态;
  • 本问题的状态:考虑偷取 [x ... n - 1] 范围里的房子;
状态转移
  • 根据状态的定义,决定状态的转移;
  • 状态转移方程式:f(0) = max { v(0) + f(2), v(1) + f(3), v(2) + f(4), ... , v(n-3) + f(n-1), v(n-2), v(n-1)
  • 对于每一个动态规划问题,都要从状态和状态转移的角度思考;

解法思路(一)

思路描述
  • 在一个数组上,取元素,取的元素不能相邻,要取到的元素之和最大的那个解;
  • 首先要找出所有的取法;
所有的取法
  • 如果第一次取第 0 个元素,那么第二次只能从第 2 个元素开始取;
  • 如果第一次取第 1 个元素,那么第二次只能从第 3 个元素开始取;
  • ...
  • 根据以上规律,画出数组长度为 5 的抢劫递归树,如下:
8195388-40701c340106ac1a.png
算法过程.png
  • 求上图到叶子节点所有路径之和最大的路径,把这条路径的和返回;
动态规划的基础
  • 图中用相同颜色标出的子问题是相同的;
  • 由于出现了相同子问题,就可以用记忆化递归;
  • 通过观察记忆化递归的代码,发现用于记忆的数组的变化规律,从这个规律入手,写出动态规划的代码;

解法实现(一)递归

实现细节
  • 参照递归树,阅读代码;
package leetcode._198;

public class Solution198_1 {

    public int rob(int[] nums) {
        return rob(nums, 0);
    }

    private int rob(int[] nums, int index) {

        if (index >= nums.length) {
            return 0;
        }

        int res = 0;
        for (int i = index; i < nums.length; i++) {
            res = Math.max(res, nums[i] + rob(nums, i + 2));
        }

        return res;
    }

    public static void main(String[] args) {

        int[] nums = {2, 7, 9, 3, 1};
        Solution198_1 s = new Solution198_1();
        int res = s.rob(nums);
        System.out.println(res);

    }

}

解法实现(二)记忆化递归

实现细节
  • 参照递归树,阅读代码;
package leetcode._198;

import java.util.Arrays;

public class Solution198_2 {

    private int[] memo;

    public int rob(int[] nums) {

        memo = new int[nums.length];
        Arrays.fill(memo, -1);

        return rob(nums, 0);
    }

    private int rob(int[] nums, int index) {

        if (index >= nums.length) {
            return 0;
        }

        if (memo[index] != -1) {
            return memo[index];
        }

        int res = 0;
        for (int i = index; i < nums.length; i++) {
            res = Math.max(res, nums[i] + rob(nums, i + 2));
        }
        memo[index] = res;

        return res;
    }

    public static void main(String[] args) {

        int[] nums = {2, 7, 9, 3, 1};
        Solution198_1 s = new Solution198_1();
        int res = s.rob(nums);
        System.out.println(res);

    }

}

解法实现(三)动态规划

实现细节
  • 参照记忆化递归的代码,观察数组 memo 的变化规律,观察以下动态规划的代码;
package leetcode._198;

public class Solution198_3 {

    private int[] memo;

    public int rob(int[] nums) {

        int n = nums.length;
        memo = new int[n];

        if (n <= 0) {
            return 0;
        }

        memo[n - 1] = nums[n - 1];

        int res = 0;
        for (int i = n - 2; i >= 0; i--) {
            for (int j = i; j < n; j++) {
                memo[i] = Math.max(memo[i], j + 2 < n ? nums[j] + memo[j + 2] : nums[j]);
            }
        }

        return memo[0];
    }

    public static void main(String[] args) {

        int[] nums = {2, 7, 9, 3, 1};
        Solution198_3 s = new Solution198_3();
        int res = s.rob(nums);
        System.out.println(res);

    }

}

返回 LeetCode [Java] 目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值