力扣-337-打家劫舍Ⅲ

直达链接

打家劫舍的区别在于,打家劫舍原题是数组,这里是二叉树
而且这里只能由根节点开始
回顾一下之前的思路,看有什么异同、是否还适用

访问任意节点,当前节点可获得最大价值为:

  1. 不要当前节点值,那么收益便是:截至上一个节点的值(上一个节点?!这里只能知道子节点不能知道父节点)
  2. 要当前值,那么获得的价值便是:当前价值+截至上上个节点获取的价值(上个都没法知道,怎么知道上上个?!)
  3. 这里是树形路径而不是单一线性路径,而且入口是根节点,无法“自底向下”

重新想一想
自顶向下

  1. 要当前节点价值,那么最大价值为:当前节点价值+截至左右子节点的所有子节点所能获取的最大价值
  2. 不要当前节点,可获得最大价值为:截至左右子节点所能获得的最大价值

感觉很像是“有状态”的搜索,自顶向下规划,递归……

官方题解

这边用到了两个数据结构来动态规划

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);
	}
};

很多时候动态规划并不需要知道过往所有的值,只需要前一步的值就够了

posted @ 2022-08-08 16:17  YaosGHC  阅读(32)  评论(0)    收藏  举报