动态规划——House Robber (小偷问题)

House Robber (小偷问题):

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

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

动态规划的的四个解题步骤是:

  • 定义子问题
  • 写出子问题的递推关系
  • 确定 DP 数组的计算顺序
  • 空间优化

步骤一:定义子问题
子问题是和原问题相似,但规模较小的问题。小偷问题,原问题是 “从全部房子中能偷到的最大金额”,将问题的规模缩小,子问题就是 “从 k个房子中能偷到的最大金额 ”,用 f(k)表示。

小偷问题的子问题定义

步骤二:写出子问题的递推关系
这一步是求解动态规划问题最关键的一步。

假设一共有 n个房子,每个房子的金额分别是H_0,H_1,H_{n-1}​,子问题 f(k) 表示从前 k 个房子中能偷到的最大金额。那么,偷 k个房子有两种偷法:

子问题的递推关系

k 个房子中最后一个房子是 H_{k-1}。如果不偷这个房子,那么问题就变成在前 k−1个房子中偷到最大的金额,也就是子问题 f(k−1)。如果偷这个房子,那么前一个房子H_{k-2}显然不能偷,其他房子不受影响。那么问题就变成在前 k−2k-2k−2 个房子中偷到的最大的金额。两种情况中,选择金额较大的一种结果。  

f\left ( k \right )=max\left \{ f\left ( k-1 \right ) ,H_{k-1}+f\left ( k-2 \right )\right \}

在写递推关系的时候,要注意写上 k=0k=0k=0 和 k=1k=1k=1 的基本情况:

当 k=0时,没有房子,所以 f(0)=0。
当 k=1时,只有一个房子,偷这个房子即可,所以 f(1)=H_{0}

步骤三:确定 DP 数组的计算顺序

DP 数组也可以叫”子问题数组”,因为 DP 数组中的每一个元素都对应一个子问题。

DP 数组与子问题的对应关系

那么,只要搞清楚了子问题的计算顺序,就可以确定 DP 数组的计算顺序。对于小偷问题,我们分析子问题的依赖关系,发现每个 f(k) 依赖 f(k−1)和 f(k−2)。也就是说,dp[k] 依赖 dp[k-1] 和 dp[k-2],如下图所示。

DP 数组的依赖顺序

 那么,既然 DP 数组中的依赖关系都是向右指的,DP 数组的计算顺序就是从左向右。这样我们可以保证,计算一个子问题的时候,它所依赖的那些子问题已经计算出来了。

​题解代码如下:

class Solution {
public:
    int rob(vector<int>& nums) {
        if (nums.size() == 0) {
            return 0;
        }
        int n = nums.size();
        vector<int>dp(n + 1, 0);
        dp[0] = 0;
        dp[1] = nums[0];
        for (int k = 2;k <= n;k++)
        {
            dp[k] = max(dp[k - 1], dp[k - 2] + nums[k - 1]);
        }
        return dp[n];
    }
};

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值