打家劫舍3

今天和打家讲一下打家劫舍3

题目:

题目链接:337. 打家劫舍 III - 力扣(LeetCode)

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为root。

除了 root 之外,每栋房子有且只有一个"父"房子与之相连。一番侦察之后,聪明的小偷意识到"这个地方的所有房屋的排列类似于一棵二叉树"。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额

示例 1:

复制代码
输入: root = [3,2,3,null,3,null,1]
输出: 7 
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7

示例 2:

复制代码
输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9

代码:

cpp 复制代码
class Solution {
    #define x first
    #define y second 
    typedef pair<int,int> PII;
public:
    int rob(TreeNode* root) {
        if(!root) return 0;
        auto t =dfs(root);
        int ans=max(t.x,t.y);
        return ans;
    }

  inline  PII dfs(TreeNode* u){//pair<tou,butou>
        if(!u) return {0,0};
        int stolen=u->val;
        PII l = dfs(u->left);
        PII r = dfs(u->right);
        stolen +=l.y+r.y ;
        int nostolen=0;
        if (l.x>l.y)  nostolen=l.x;
        else nostolen =l.y;

        if (r.x>r.y)  nostolen +=r.x;
        else nostolen +=r.y;
        
        PII t={stolen , nostolen};
        return t;
    }
};

运行效率:

宏定义与类型别名:

cpp 复制代码
#define x first    // 用 x 表示 pair 的 first(偷当前节点的最大值)
#define y second   // 用 y 表示 pair 的 second(不偷当前节点的最大值)
typedef pair<int, int> PII;

PII 是一个二元组,存储两个状态:

cpp 复制代码
first(x):偷当前节点时的最大收益。
second(y):不偷当前节点时的最大收益。

主函数 rob:

若树为空,直接返回0。

调用dfs递归计算根节点的两种状态,返回较大值。

cpp 复制代码
int rob(TreeNode* root) {
    if (!root) return 0;
    auto t = dfs(root);
    return max(t.x, t.y); // 最终结果取根节点偷或不偷的最大值
}

递归函数 dfs:

cpp 复制代码
PII dfs(TreeNode* u) {
    if (!u) return {0, 0}; // 空节点返回 {0,0}
    int stolen = u->val;   // 偷当前节点
    PII l = dfs(u->left);  // 递归左子树
    PII r = dfs(u->right); // 递归右子树
    stolen += l.y + r.y;   // 偷当前节点时,左右子节点必须不偷
    // 不偷当前节点时,左右子节点可偷或不偷(取各自最大值相加)
    int nostolen = max(l.x, l.y) + max(r.x, r.y); 
    return {stolen, nostolen}; // 返回当前节点的两种状态
}
  • 偷当前节点 (stolen):

    当前节点的值 + 左子节点不偷的最大值(l.y) + 右子节点不偷的最大值(r.y)。

  • 不偷当前节点 (nostolen):

    左子节点偷或不偷的最大值(max(l.x, l.y)) + 右子节点偷或不偷的最大值(max(r.x, r.y))。


动态规划逻辑:

状态定义:

代码通过后序遍历 + 动态规划实现,每个节点返回两个状态:

  • stolen(偷当前节点的最大收益)
  • not_stolen(不偷当前节点的最大收益)
    最终取根节点的两种状态的最大值。

对每个节点 u,定义两个状态:

stolen(偷 u 时的最大收益)。

nostolen(不偷 u 时的最大收益)。

状态转移:

偷当前节点

不能偷子节点,因此收益为:

stolen=u.val+left.y+right.ystolen=u.val+left.y+right.y

不偷当前节点

子节点可自由选择偷或不偷,取最大值相加:

nostolen=max⁡(left.x,left.y)+max⁡(right.x,right.y)nostolen=max(left.x,left.y)+max(right.x,right.y)

递归过程:

  • 从叶子节点向上递推,确保每个节点的状态仅依赖子节点的状态。

示例分析:

假设二叉树如下:

复制代码
     3
    / \
   2   3
    \   \ 
     3   1

叶子节点(值为3和1的节点):

  • stolen = 3(或1),nostolen = 0(不偷叶子节点时无收益)。

中间节点(值为2的节点):

  • 偷:2 + 0(左子不偷) + 0(右子不偷) = 2。
  • 不偷:max(0, 3)(左子偷或不偷的最大值) + 0 = 3。

根节点(值为3):

  • 偷:3 + 3(左子不偷) + 1(右子不偷) = 7。
  • 不偷:max(2, 3)(左子状态) + max(3, 1)(右子状态) = 3 + 3 = 6。

最终结果:max(7, 6) = 7。

关键点:

  • 确保子节点状态传递正确,尤其是"不偷当前节点"时,子节点可自由选择偷或不偷。

  • 后序遍历确保先处理子节点再处理父节点。

相关推荐
MSTcheng.几秒前
【C++STL】map / multimap 保姆级教程:从底层原理到实战应用!
开发语言·c++·stl·map·红黑树
努力学算法的蒟蒻2 分钟前
day41(12.22)——leetcode面试经典150
算法·leetcode·面试
liliangcsdn2 分钟前
Python拒绝采样算法优化与微调模拟
人工智能·算法·机器学习
Christo34 分钟前
2024《A Rapid Review of Clustering Algorithms》
人工智能·算法·机器学习·数据挖掘
AndrewHZ7 分钟前
【图像处理基石】图像梯度:核心算法原理与经典应用场景全解析
图像处理·算法·计算机视觉·cv·算子·边缘提取·图像梯度
让学习成为一种生活方式7 分钟前
组蛋白短链酰化修饰--文献精读187
算法
ULTRA??9 分钟前
基于range的函数式编程C++,python比较
c++·python·kotlin·c++20
fei_sun11 分钟前
数字图像处理
人工智能·算法·计算机视觉
Tisfy15 分钟前
LeetCode 960.删列造序 III:动态规划(最长递增子序列)
算法·leetcode·动态规划·字符串·题解·逆向思维
多米Domi01117 分钟前
0x3f第十天复习(考研日2)(9.18-12.30,14.00-15.00)
python·算法·leetcode