【LeetCode & 剑指offer刷题】动态规划与贪婪法题8:House Robber(系列)

【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)

House Robber(系列)

 House Robber
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.

C++
 
/*
问题:一个窃贼要盗窃一条街上的房子。每个房子里有固定数量的钱,窃贼如果在一个晚上偷了两个相邻的房子就会触发警报,警察就会来抓这个窃贼。那么,怎样一个盗窃方案能使窃贼拿到的钱最多且不触发警报。
( 本质相当于在一列数组中取出一个或多个不相邻数,使其和最大
*/
/*
方法一:动态规划
dp[i]表示到i位置时不相邻数能形成的最大和
对于某个位置i,可以取也可以不取,
如果取(不能相邻取,故只能加上前面的前面的dp值),则为dp[i-2] + num[i],如果不取,则为dp[i-1]
所以递推公式为dp[i] = max(dp[i-2] + num[i], dp[i-1])
*/
class Solution
{
public:
    int rob(vector<int> &num)
    {
        if(num.empty()) return 0;
        if(num.size() == 1) return num[0];
       
        vector<int> dp(num.size());//对于数组类的题,如果i表示下标,0序分析即可,可以不用多开辟一个空间
        dp[0] = num[0]; //初始化前两个dp值 
        dp[1] = max(num[0], num[1]);
       
        for (int i = 2; i < num.size(); i++) //从位置2开始扫描
        {
            dp[i] = max(dp[i-2] + num[i], dp[i-1]);
        }
        return dp.back();
    }
};
/*
方法二:动态规划,空间的优化
分别维护两个变量a和b,然后按奇偶分别来更新a和b,这样就可以保证组成最大和的数字不相邻
遍历奇数序列,遍历偶数序列,交叉比较(每次判断是否加当前数)
时间复杂度O(n),空间复杂度O(1)
*/
class Solution
{
public:
    int rob(vector<int>& nums)
    {
        int a=0,b=0; //a为偶数累加器,b为奇数累加器
      
        for(int i=0; i<nums.size(); i++)
        {
            if(i%2==0) a=max(a+nums[i],b); //选择加当前数或者上一个奇数累加器值(此表达式的可以约束相邻的两数不会同时取到)
            else b = max(a, b+nums[i]); //选择加当前数或者上一个偶数累加器值
        }
      
        return max(a, b);
    }
};
 
213. House Robber II
 All houses at this place are arranged in a circle.
Example 1:
Input: [2,3,2]
Output: 3
Explanation: You cannot rob house 1 (money = 2) and then rob house 3 (money = 2),
because they are adjacent houses.
Example 2:
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.

 
/*
问题:打家劫舍2(排成圆圈)
现在房子排成了一个圆圈,则如果抢了第一家,就不能抢最后一家,因为首尾相连了,所以第一家和最后一家只能抢其中的一家,或者都不抢
方法:动态规划
如果我们把第一家和最后一家分别去掉,各算一遍能抢的最大值
然后比较两个值取其中较大的一个即为所求
*/
class Solution
{
public:
    int rob(vector<int>& nums)
    {
        if(nums.empty()) return 0;
        if(nums.size() == 1) return nums[0];
       
        return max(rob(nums, 0, nums.size() - 1), rob(nums, 1, nums.size()));
    }
   
    int rob(vector<int> &nums, int left, int right)
    {
        if (right - left <= 1)
            return nums[left];
       
        vector<int> dp(right);
        dp[left] = nums[left]; //初始化前两个dp值
        dp[left + 1] = max(nums[left], nums[left + 1]);
       
        for (int i = left + 2; i < right; i++)
        {
            dp[i] = max(nums[i] + dp[i - 2], dp[i - 1]);
        }
       
        return dp.back();
    }
};
 
337. House Robber III
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the "root." Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that "all houses in this place forms a binary tree". It will automatically contact the police if two directly-linked houses were broken into on the same night.
Determine the maximum amount of money the thief can rob tonight without alerting the police.
Example 1:
3
/ \
2 3
\     \
3     1
Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
 
Example 2:
    3
    / \
  4     5
 / \     \
1 3      1
Maximum amount of money the thief can rob = 4 + 5 = 9.

 
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
/*
问题:打家劫舍3
各房子构成二叉树(每个房子有且只有一个父结点),不能同时偷两个直接连接的房子
方法:
res[0]表示不包含当前节点值的最大值,res[1]表示包含当前值的最大值
*/
class Solution
{
public:
    int rob(TreeNode* root)
    {
        vector<int> res = dfs(root); //从根结点开始递归遍历
        return max(res[0], res[1]); //返回包含或不包含根结点时的最大值
    }
    vector<int> dfs(TreeNode *root)
    {
        if (!root) //空结点时,返回0
            return vector<int>(2);
       
        vector<int> left = dfs(root->left); //处理左子树
        vector<int> right = dfs(root->right); //处理右子树
        vector<int> res(2);
       
        //处理根结点
        res[0] = max(left[0], left[1]) + max(right[0], right[1]); //不包含根结点的情况,则左结点和右结点可以包含也可以不包含
        res[1] = left[0] + right[0] + root->val;//包含根结点的情况,则不能包含左结点和右结点
        return res;
    }
};
213. House Robber II
 

 

posted @ 2019-01-06 16:46  wikiwen  阅读(784)  评论(0编辑  收藏  举报