打家劫舍问题总结

打家劫舍问题是经典的动态问题

198. 打家劫舍

第一道比较简单,dp[i]表示偷到第i家的时候最大收益,有两种选择,一种是偷第i家一种是不偷,偷的话收益为dp[i - 2] + value[i],即偷第i - 2家的最大收益加上第i家的收益,不偷的收益为dp[i - 1],这个好理解吧。

所以递推公式为:

dp[i] = max(dp[i - 2] + value[i], dp[i - 1])

从递推公式也可以看出了要初始化dp[0]和dp[1]。最终代码为:

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

还有一个更简易的版本,即只考虑每家偷不偷,偷的收益记录下来,不偷的收益记录下来,代码为:

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

这种写法空间复杂度降为O(1)。

213. 打家劫舍 II

这道题如果会做上面那道题也比较简单,环状影响的主要就是第一个和最后一个,对中间的递推方程没有影响,当推导最后一个时,要考虑第一个有没有偷,如果没偷那最后一个正常递归,如果偷了那最后一个就不能再偷了,所以分两种情况考虑即可,即分为偷第一个和不偷第一个,然后比较两种情况哪种情况收益大就行了。

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

337. 打家劫舍 III

这个题看起来有点难度,其实和前两道题差不多,只是在树上进行动态规划而已,每一个节点有两种情况,一种是不偷,即直接取左右子节点金额的和,一种是偷,这个时候就要取孙子节点的和了,所以递归的时候要传递两个参数,一个是左右子节点的和,一个是本身该节点的最大收益,传给父节点,父节点再利用左右子树共四个参数来求该节点的最大收益,然后再传递相应参数。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> traversal(TreeNode* node) {
        vector<int> temp(2, 0);
        if (!node->left && !node->right) {
            temp[1] = node->val;
        }
        else {
            vector<int> left_num(2, 0);
            vector<int> right_num(2, 0);
            if (node->left)
                left_num = traversal(node->left);
            if (node->right)
                right_num = traversal(node->right);
            temp[0] = left_num[1] + right_num[1];
            temp[1] = max(left_num[1] + right_num[1], node->val + left_num[0] + right_num[0]);
        }
        return temp;
    }
    int rob(TreeNode* root) {
        vector<int> result = traversal(root);
        return result[1];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值