对于动态规划二叉树问题,与之前的动态规划的问题有明显不同:以往的动规问题遍历的一般都是一维或者二维数组,但是对于这种树形DP问题,需要遍历的就是一颗二叉树。一般对于二叉树的遍历有前/中/后 序遍历(深度优先搜索)和层序遍历(广度优先搜索)。
一 典型问题
小偷⼜发现了⼀个新的可⾏窃的地区。这个地区只有⼀个入口,我们称之为“根”。 除了“根”之外,每栋房⼦有且只有⼀个“父“房子与之相连。⼀番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于⼀棵⼆叉树”。 如果两个直接相连的房子在同⼀天晚上被 打劫,房屋将自动报警。计算在不触动警报的情况下,小偷⼀晚能够盗取的最高金额。
如:
输入:[3,2,3,null,3,null,1]
输出:7(红色的节点相加:3+3+1=7)
二 分析
1.递归函数返回值:递归函数返回的是两种情况下的打劫金额值,即被遍历节点处打劫或不打劫两种情况下,以该节点作为根节点的树所能获得最大打劫金额。因此,递归函数返回的是访问该节点情况下和不访问该节点情况下的两种打劫金额值,因此递归函数以一个vector数组作为返回值:
vector<int> rob_bianli(TreeNode* cur);
函数返回值中,第一项为打劫该节点所能获得的以该节点为根节点的最大金额数,第二项为不打劫该节点所能获得的以该节点为根节点的最大金额数:
return {val_noncur, val_cur};
2.递推公式:该节点所能打劫的金额分为两种情况,一种是打劫该节点情况下的最大金额:打劫该节点的话,就不能打劫与之相连的左右孩子,则最大金额是该节点的金额值+左孩子返回的不打劫左孩子的金额数+右孩子返回的不打劫右孩子的金额数:
int val_cur = cur->val + left[0] + right[0];//
一种是不打劫该节点情况下的最大金额:左孩子返回的打劫或者不打劫该左孩子节点两种情况下的最大值+右孩子返回的打劫或者不搭界该右孩子节点两种情况下的最大值:
int val_noncur = max(left[0], left[1]) + max(right[0], right[1]);
3.遍历顺序:本问题是采用后序遍历对二叉树进行访问的:
vector<int> left = rob_bianli(cur->left);//左
vector<int> right = rob_bianli(cur->right);//右
//中
三 代码
int rob(TreeNode* root) {
vector<int> result = rob_bianli(root);
int max_value = max(result[0], result[1]);
return max_value;}
vector<int> rob_bianli(TreeNode* cur){
if(cur == nullptr) return{0, 0};
vector<int> left = rob_bianli(cur->left);
vector<int> right = rob_bianli(cur->right);
//遍历当前节点cur
int val_cur = cur->val + left[0] + right[0];
//不遍历当前节点
int val_noncur = max(left[0], left[1]) + max(right[0], right[1]);
return {val_noncur, val_cur};}