【每日算法Day 106】打家劫舍系列最后一弹,撑住你就赢了!

简介: 【每日算法Day 106】打家劫舍系列最后一弹,撑住你就赢了!

题目链接

LeetCode 337. 打家劫舍 III[1]

往期回顾:打家劫舍 I :

【每日算法Day 104】偷电瓶的周某今天放出来了,还不赶紧做这道题防范一下![2]

往期回顾:打家劫舍 II :

【每日算法Day 105】打家劫舍第二弹:看好你的电瓶车![3]

题目描述

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为。除了之外,每栋房子有且只有一个房子与之相连。一番侦察之后,聪明的小偷意识到这个地方的所有房屋的排列类似于一棵二叉树。如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例1

输入:
[3,2,3,null,3,null,1]
     3
    / \
   2   3
    \   \ 
     3   1
输出:
7
解释:
小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例2

输入:
[3,4,5,1,3,null,1]
     3
    / \
   4   5
  / \   \ 
 1   3   1
输出:
9
解释:
小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

题解

这次是在一棵树上偷窃了,做法还是一样。对于结点 r 来说,我们还是分为偷和不偷两种情况。

如果偷的话,它的左右儿子就不能偷了,所以最大价值就是左儿子不偷的最大价值,加上右儿子不偷的最大价值,再加上 r 的价值。

而如果不偷的话,最大价值就是左儿子偷或不偷的最大价值,加上右儿子偷或不偷的最大价值。

因为需要用到儿子结点偷和不偷两个价值,所以需要在 dfs 时返回两个值,用来表示偷和不偷两个最大价值,具体实现时用 pair 来表示。

可能有人会用另一种实现方式,用 dfs0 表示不偷的最大价值,dfs1 表示偷的最大价值,然后两个函数互相调用。注意这样理论上是可行的,但是会产生很多重复计算,最终会超时。所以这种方法需要记忆化搜索,比较麻烦,需要用 map<TreeNode*, int> 来保存每个结点的答案。

代码

复杂实现方式(c++)

class Solution {
public:
    unordered_map<TreeNode*, int> dp0, dp1;
    int dfs0(TreeNode* root) {
        if (!root) return 0;
        if (dp0.find(root) != dp0.end()) return dp0[root];
        int res = 0;
        res += max(dfs0(root->left), dfs1(root->left));
        res += max(dfs0(root->right), dfs1(root->right));
        return dp0[root]=res;
    }
    int dfs1(TreeNode* root) {
        if (!root) return 0;
        if (dp1.find(root) != dp1.end()) return dp1[root];
        int res = root->val;
        res += dfs0(root->left);
        res += dfs0(root->right);
        return dp1[root]=res;
    }
    int rob(TreeNode* root) {
        if (!root) return 0;
        dp0.clear();
        dp1.clear();
        return max(dfs0(root), dfs1(root));
    }
};

精简实现方式(c++)

class Solution {
public:
    typedef pair<int, int> pii;
    pii dfs(TreeNode* root) {
        if (!root) return {0, 0};
        pii l = dfs(root->left);
        pii r = dfs(root->right);
        int r0 = max(l.first, l.second) + max(r.first, r.second);
        int r1 = l.first + r.first + root->val;
        return {r0, r1};
    }
    int rob(TreeNode* root) {
        pii res = dfs(root);
        return max(res.first, res.second);
    }
};
相关文章
|
16天前
|
机器学习/深度学习 人工智能 算法
基于DCT和扩频的音频水印嵌入提取算法matlab仿真
本文介绍了结合DCT和扩频技术的音频水印算法,用于在不降低音质的情况下嵌入版权信息。在matlab2022a中实现,算法利用DCT进行频域处理,通过扩频增强水印的隐蔽性和抗攻击性。核心程序展示了水印的嵌入与提取过程,包括DCT变换、水印扩频及反变换步骤。该方法有效且专业,未来研究将侧重于提高实用性和安全性。
|
1月前
|
机器学习/深度学习 算法
【MATLAB】GA_BP神经网络时序预测算法
【MATLAB】GA_BP神经网络时序预测算法
35 8
|
3天前
|
存储 算法
m基于LDPC编译码的matlab误码率仿真,对比SP,MS,NMS以及OMS四种译码算法
MATLAB 2022a仿真实现了LDPC译码算法比较,包括Sum-Product (SP),Min-Sum (MS),Normalized Min-Sum (NMS)和Offset Min-Sum (OMS)。四种算法在不同通信场景有各自优势:SP最准确但计算复杂度高;MS计算复杂度最低但性能略逊;NMS通过归一化提升低SNR性能;OMS引入偏置优化高SNR表现。适用于资源有限或高性能需求的场景。提供的MATLAB代码用于仿真并绘制不同SNR下的误码率曲线。
18 3
|
6天前
|
算法 数据安全/隐私保护 计算机视觉
基于DCT变换的彩色图像双重水印嵌入和提取算法matlab仿真
**算法摘要:** - 图形展示:展示灰度与彩色图像水印应用,主辅水印嵌入。 - 软件环境:MATLAB 2022a。 - 算法原理:双重水印,转换至YCbCr/YIQ,仅影响亮度;图像分割为M×N块,DCT变换后嵌入水印。 - 流程概览:两步水印嵌入,每步对应不同图示表示。 - 核心代码未提供。
|
6天前
|
机器学习/深度学习 算法 数据可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化
10 0
|
6天前
|
算法 TensorFlow 算法框架/工具
基于直方图的图像阈值计算和分割算法FPGA实现,包含tb测试文件和MATLAB辅助验证
这是一个关于图像处理的算法实现摘要,主要包括四部分:展示了四张算法运行的效果图;提到了使用的软件版本为VIVADO 2019.2和matlab 2022a;介绍了算法理论,即基于直方图的图像阈值分割,通过灰度直方图分布选取阈值来区分图像区域;并提供了部分Verilog代码,该代码读取图像数据,进行处理,并输出结果到&quot;result.txt&quot;以供MATLAB显示图像分割效果。
|
7天前
|
算法 搜索推荐 数据挖掘
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集
17 0
|
8天前
|
数据采集 机器学习/深度学习 存储
MATLAB用改进K-Means(K-均值)聚类算法数据挖掘高校学生的期末考试成绩
MATLAB用改进K-Means(K-均值)聚类算法数据挖掘高校学生的期末考试成绩
14 0
|
9天前
|
算法 数据安全/隐私保护 数据格式
基于混沌序列的图像加解密算法matlab仿真,并输出加解密之后的直方图
该内容是一个关于混沌系统理论及其在图像加解密算法中的应用摘要。介绍了使用matlab2022a运行的算法,重点阐述了混沌系统的特性,如确定性、非线性、初值敏感性等,并以Logistic映射为例展示混沌序列生成。图像加解密流程包括预处理、混沌序列生成、数据混淆和扩散,以及密钥管理。提供了部分核心程序,涉及混沌序列用于图像像素的混淆和扩散过程,通过位操作实现加密。
|
9天前
|
数据采集 算法 数据可视化
MATLAB、R用改进Fuzzy C-means模糊C均值聚类算法的微博用户特征调研数据聚类研究
MATLAB、R用改进Fuzzy C-means模糊C均值聚类算法的微博用户特征调研数据聚类研究
16 1