解答
动态规划(树形DP)
打劫一个树的最大收益,是 robIncludeRoot 和 robExcludeRoot 中的较大者。
即每个子树都有两个状态下的最优解:没打劫 root、和有打劫 root 下的最优解。
有两个变量共同决定一个状态:1、代表不同子树的 root 节点、2、是否打劫了 root。
没打劫根节点,则左右子树的根节点可打劫可不打劫:
dp[0] = 左子树的两个状态的较大值 + 右子树的两个状态的较大值。
打劫了根节点,则左右子树的根节点不能打劫:
dp[1] = root.val + 左子树的 [0] 状态 + 右子树的 [0] 状态。
#include <vector>
using namespace std;
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:
int rob(TreeNode* root) {
// 每个节点都有两种选择:偷、不偷
// 从叶子节点开始判断,直到来到根节点
vector<int> res = robNode(root);
// 选择当前节点偷和不偷抉择时的较大值
return max(res[0],res[1]);
}
vector<int> robNode(TreeNode* root)
{
// dp[0] 表示的是以当前 node 为根节点的子树能够偷取的最大金额,并且此时采取【不偷】 node 这个节点的策略
// dp[1] 表示的是以当前 node 为根节点的子树能够偷取的最大金额,并且此时采取【偷】 node 这个节点的策略
vector<int> dp(2);
// 递归终止条件
// 即在叶子节点的时候,叶子节点的左右子节点为 null
// 继续调用下去就会返回给叶子节点,它的左右子树的选择的金额都是 0
if(root==nullptr) return dp;
// 递归计算左子节点的选择
vector<int> leftVal = robNode(root->left);
// 递归计算右子节点的选择
vector<int> rightVal = robNode(root->right);
// 如果选择不偷,那么可以偷左右子节点,这个时候的最大金额为左右子节点最大金额之和
dp[0] = max(leftVal[0],leftVal[1])+max(rightVal[0],rightVal[1]);
// 1、如果选择偷,那么左子节点不能偷,这个时候需要获取不偷左子节点时的最大金额
// 2、如果选择偷,那么右子节点不能偷,这个时候需要获取不偷右子节点时的最大金额
// 3、再加上当前节点的金额
dp[1] = leftVal[0]+rightVal[0]+root->val;
return dp;
}
};