题目描述
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例1
输入:[3,2,3,null,3,null,1]
3 / \ 2 3 \ \ 3 1
输出: 7
解释:小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
动态规划
根据题意,对于每一个节点来说,都有选和不选两种状态,约束条件是同一条边连接的两点不能同时选中。
我们假设 dp[i]
表示选择节点i时,i
的子树上可以获得的最大价值,no[i]
表示不选择节点i时,i
的子树上可以获得的最大价值。
那么对于一个节点i
来说,假设它的左右儿子分别为left
, right
:
- 1.
dp[i] = no[left] + no[right] + value[i];
选择父节点,那么子节点都不能选 - 2.
no[i] = max(dp[l],no[l]) + max(dp[r], no[r]);
不选择父节点,那么左右儿子的选择情况有四种,选择其中最大的即可
时间复杂度
因为每个节点被遍历一次,所以时间复杂度是$O(N)$的,同时空间复杂度也是$O(N)$的。
C++代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
map<TreeNode*,int> dp; //选择当前节点
map<TreeNode*,int> no; //不选择当前节点
int rob(TreeNode* root) {
dp[nullptr] = no[nullptr] = 0;
dfs(root);
return max(dp[root],no[root]);
}
void dfs(TreeNode* root){
if(root == nullptr) return ;
dfs(root->left);
dfs(root->right);
// if(root->left && root->right)
dp[root] = no[root->left] + no[root->right] + root->val;
no[root] = max(dp[root->left],no[root->left]) + max(dp[root->right],no[root->right]);
}
};