打家劫舍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。

关键点:

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

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

相关推荐
小蜗牛~向前冲21 分钟前
MFC线程安全案例
c++·mfc
_不会dp不改名_28 分钟前
leetcode_80删除有序数组中的重复项 II
数据结构·算法·leetcode
√尖尖角↑29 分钟前
力扣——【104. 二叉树的最大深度】
python·算法·leetcode
幸运小圣29 分钟前
LeetCode热题100- 轮转数组【JavaScript讲解】
javascript·算法·leetcode
MYX_30929 分钟前
第七节 文件与流
开发语言·c++·学习·算法
Lostgreen36 分钟前
解密K-means:简单易懂的算法指南
算法·机器学习·kmeans
~怎么回事啊~2 小时前
chrome-mojo C++ Bindings API
开发语言·c++
fpcc2 小时前
C++20中的std::atomic_ref
c++·c++20
惊岁晚2 小时前
【R】Dijkstra算法求最短路径
算法·贪心算法·r语言
Mekeater3 小时前
手写一个C++ Android Binder服务及源码分析
android·c++·binder·remote·iservicemanager·bpbinder·bprefbase