力扣-337-打家劫舍Ⅲ
直达链接
与打家劫舍的区别在于,打家劫舍原题是数组,这里是二叉树
而且这里只能由根节点开始
回顾一下之前的思路,看有什么异同、是否还适用
访问任意节点,当前节点可获得最大价值为:
- 不要当前节点值,那么收益便是:截至上一个节点的值(上一个节点?!这里只能知道子节点不能知道父节点)
- 要当前值,那么获得的价值便是:当前价值+截至上上个节点获取的价值(上个都没法知道,怎么知道上上个?!)
- 这里是树形路径而不是单一线性路径,而且入口是根节点,无法“自底向下”
重新想一想
自顶向下
- 要当前节点价值,那么最大价值为:当前节点价值+截至左右子节点的所有子节点所能获取的最大价值
- 不要当前节点,可获得最大价值为:截至左右子节点所能获得的最大价值
感觉很像是“有状态”的搜索,自顶向下规划,递归……
官方题解
这边用到了两个数据结构来动态规划
class Solution { public: 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) {} }; unordered_map <TreeNode*, int> f, g; void dfs(TreeNode* node) { if (!node) return; dfs(node->left); dfs(node->right); f[node] = node->val + g[node->left] + g[node->right]; g[node] = max(f[node->left], g[node->right]) + max(f[node->right], g[node->left]); } int rob(TreeNode* root) { dfs(root); return max(f[root], g[root]); } };
这确实是没见过的新东西——二叉树的动态规划
再看看“小小的空间优化“:
struct SubtreeStatus { int selected; int notSelected; }; class Solution { public: SubtreeStatus dfs(TreeNode* node) { if (!node) return{0,0}; auto l = dfs(node->left); auto r = dfs(node->right); int selected = node->val + l.notSelected + r.notSelected; int notSlected = max(l.selected, l.notSelected) + max(r.selected, r.notSelected); return {selected,notSelected}; } int rob(TreeNode* root) { auto rootStatus = dfs(root); return max(rootStatus.notSelected,rootStatus.selected); } };
很多时候动态规划并不需要知道过往所有的值,只需要前一步的值就够了
本文作者:YaosGHC
本文链接:https://www.cnblogs.com/yaocy/p/16527828.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步