本周上课的重点依然为动态规划,主要讲树状dp、状态压缩dp、Floyd-Warshall算法。这是一道典型的树状dp的题,难度为Medium。
原题:
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.
思路:题目的大意是,在一棵树中,连接一条边的两个点至多只能有一个点能被偷窃,如果两个点同时被偷窃,就会报警。显然,这是一道几乎裸的树状dp,可以用树的递归遍历来做。以某一顶点为根,其所在的树能偷窃的最大值,就是该问题的子问题。一个父节点所在的树能偷窃的最大值,肯定与其左右子树能偷窃的最大值有关,这就是动态规划!关键是到底有什么关系呢?求一个父节点所在的树能偷窃的最大值,分为两种情况:一是,父节点本身包括,这时左右孩子节点肯定都不包括;二是,父节点本身不包括,这时左右孩子节点可包括可不包括。这两种情况的最大值就是我们要求的结果。因此,对于某个节点,我们只需维护该节点的两个值。用include保存包括该节点的情况下以该节点为根的树能偷窃的最大值;用not_include保存不包括该节点的情况下以该节点为根的树能偷窃的最大值。某节点include值=lefttree.not_include+righttree.not_include+该节点val;某节点not_include值=max(lefttree.not_include,lefttree.include)+max(righttree.not_include,righttree.include)。以上其实就是树状dp的状态转移方程。
代码:9ms通过
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
//保存以某个节点为根的树所能偷窃的最大值
//include表示在包含该节点的情况下的最大值,not_include表示在不包含该节点的情况下的最大值
struct result
{
int include,not_include;
};
class Solution {
result find(TreeNode* root){
result res;
//节点为叶子结点的情况
if(!root){
res.include=0;
res.not_include=0;
return res;
}else{
//非叶子结点的情况
result ltree,rtree;
ltree=find(root->left);
rtree=find(root->right);
res.include=ltree.not_include+rtree.not_include+root->val;
res.not_include=max(ltree.not_include,ltree.include)+max(rtree.not_include,rtree.include);
return res;
}
}
public:
int rob(TreeNode* root) {
result res;
res=find(root);
return max(res.include,res.not_include);
}
};
ps:本题还要注意递归函数的效率问题。以下代码算法思路和上述ac代码是完全一样的,仅仅因为递归函数写得不够精简的问题,就超时了!代码如下:
struct result
{
int include,not_include;
};
class Solution {
public:
result find(TreeNode* root){
result res;
if(!root){
res.include=0;
res.not_include=0;
return res;
}else{
//每层递归都包含四个子函数,递归规模指数增长!
int left_not=find(root->left).not_include;
int left_in=find(root->left).include;
int right_not=find(root->right).not_include;
int right_in=find(root->right).include;
res.include=left_not+right_not+root->val;
res.not_include=max(left_not,left_in)+max(right_not,right_in);
return res;
}
}
int rob(TreeNode* root) {
result res;
res=find(root);
return max(res.include,res.not_include);
}
};