198/213/337 House Robber

来源:互联网 发布:淘宝上假货卖正品价格 编辑:程序博客网 时间:2024/06/05 18:54

198 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.

这道题的本质相当于在一列数组中取出一个或多个不相邻数,使其和最大。那么我们对于这类求极值的问题首先考虑动态规划Dynamic Programming来解,我们维护一个一位数组dp,其中dp[i]表示到i位置时不相邻数能形成的最大和,那么递推公式怎么写呢,我们先拿一个简单的例子来分析一下,比如说nums为{3, 2, 1, 5},那么我们来看我们的dp数组应该是什么样的,首先dp[0]=3没啥疑问,再看dp[1]是多少呢,由于3比2大,所以我们抢第一个房子的3,当前房子的2不抢,所以dp[1]=3,那么再来看dp[2],由于不能抢相邻的,所以我们可以用再前面的一个的dp值加上当前的房间值,和当前房间的前面一个dp值比较,取较大值当做当前dp值,所以我们可以得到递推公式dp[i] = max(num[i] + dp[i - 2], dp[i - 1]), 由此看出我们需要初始化dp[0]和dp[1],其中dp[0]即为num[0],dp[1]此时应该为max(num[0], num[1]),代码如下:

class Solution {public:    int rob(vector<int>& nums) {        if(nums.empty()) return 0;        int n = nums.size();        if(n == 1) return nums[0];    //开始注意判断特殊情况        vector<int> dp(n, 0);        dp[0] = nums[0];        dp[1] = max(nums[0], nums[1]);        for(int i = 2; i < n; ++i) {            dp[i] = max(dp[i-1], dp[i-2] + nums[i]);  //状态转移方程        }        return dp[n - 1];    }};

还有一种解法,核心思想还是用DP,分别维护两个变量a和b,然后按奇偶分别来更新a和b,这样就可以保证组成最大和的数字不相邻,代码如下:

class Solution {public:    int rob(vector<int>& nums) {        //if(nums.empty()) return 0;        int a = 0, b = 0;        int n = nums.size();        for(int i = 0; i < n; ++i) {            if(i%2 == 0) {         //判断奇偶                a += nums[i];                a = max(a, b);     //更新最大值            } else {                b += nums[i];                b = max(a, b);            }        }        return max(a, b);    }};

213 House Robber II

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

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.

这道题是之前那道House Robber 打家劫舍的拓展,现在房子排成了一个圆圈,则如果抢了第一家,就不能抢最后一家,因为首尾相连了,所以第一家和最后一家只能抢其中的一家,或者都不抢,那我们这里变通一下,如果我们把第一家和最后一家分别去掉,各算一遍能抢的最大值,然后比较两个值取其中较大的一个即为所求。那我们只需参考之前的House Robber 打家劫舍中的解题方法,然后调用两边取较大值,代码如下:

class Solution {public:    int rob(vector<int>& nums) {        if(nums.empty()) return 0;        int n = nums.size();        if(n == 1) return nums[0];        vector<int> dp(n, 0);        int a, b;        dp[1] = nums[1];                  //去掉头节点,计算最大值        dp[2] = max(nums[1], nums[2]);        for(int i = 3; i < n; ++i) {            dp[i] = max(dp[i-1], dp[i-2]+nums[i]);        }        a = dp[n-1];        dp[0] = nums[0];                 //去掉尾节点,计算最大值        dp[1] = max(nums[0], nums[1]);        for(int i = 2; i < n-1; ++i) {            dp[i] = max(dp[i-1], dp[i-2] + nums[i]);        }        b = dp[n-2];        return max(a, b);    }};
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.

Credits:
Special thanks to @dietpepsi for adding this problem and creating all test cases.

这道题是之前那两道House Robber II和House Robber的拓展,这个小偷又偷出新花样了,沿着二叉树开始偷,碉堡了,题目中给的例子看似好像是要每隔一个偷一次,但实际上不一定只隔一个,比如如下这个例子:
4
/
1
/
2
/
3
如果隔一个偷,那么是4+2=6,其实最优解应为4+3=7,隔了两个,所以说纯粹是怎么多怎么来,那么这种问题是很典型的递归问题,我们可以利用回溯法来做,因为当前的计算需要依赖之前的结果,那么我们对于某一个节点,如果其左子节点存在,我们通过递归调用函数,算出不包含左子节点返回的值,同理,如果右子节点存在,算出不包含右子节点返回的值,那么此节点的最大值可能有两种情况,一种是该节点值加上不包含左子节点和右子节点的返回值之和,另一种是左右子节点返回值之和不包含当期节点值,取两者的较大值返回即可,但是这种方法无法通过OJ,超时了:

// Time Limit Exceededclass Solution {public:    int rob(TreeNode* root) {        if (!root) return 0;        int val = 0;        if (root->left) {            val += rob(root->left->left) + rob(root->left->right);        }        if (root->right) {            val += rob(root->right->left) + rob(root->right->right);        }        return max(val + root->val, rob(root->left) + rob(root->right));    }};

由于上面的方法超时了,所以我们必须优化上面的方法,上面的方法重复计算了很多地方,比如要完成一个节点的计算,就得一直找左右子节点计算,我们可以把已经算过的节点用哈希表保存起来,以后递归调用的时候,现在哈希表里找,如果存在直接返回,如果不存在,等计算出来后,保存到哈希表中再返回,这样方便以后再调用,参见代码如下:

class Solution {public:    int rob(TreeNode* root) {        unordered_map<TreeNode*, int> m;        return dfs(root, m);    }    int dfs(TreeNode *root, unordered_map<TreeNode*, int> &m) {        if (!root) return 0;        if (m.count(root)) return m[root];        int val = 0;        if (root->left) {            val += dfs(root->left->left, m) + dfs(root->left->right, m);        }        if (root->right) {            val += dfs(root->right->left, m) + dfs(root->right->right, m);        }        val = max(val + root->val, dfs(root->left, m) + dfs(root->right, m));        m[root] = val;        return val;    }};

下面再来看一种方法,这种方法的递归函数返回一个大小为2的一维数组res,其中res[0]表示不包含当前节点值的最大值,res[1]表示包含当前值的最大值,那么我们在遍历某个节点时,首先对其左右子节点调用递归函数,分别得到包含与不包含左子节点值的最大值,和包含于不包含右子节点值的最大值,那么当前节点的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) return vector<int>(2, 0);        vector<int> left = dfs(root->left);        vector<int> right = dfs(root->right);        vector<int> res(2, 0);        res[0] = max(left[0], left[1]) + max(right[0], right[1]);        res[1] = left[0] + right[0] + root->val;        return res;    }};
原创粉丝点击