力扣337. 打家劫舍 III

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

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

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

意思是: 带权二叉树在不能同时选择父子结点的情况下能得到最大权值是多少

错误思路: 想着用层序遍历把每层结点权值和先算出来,然后用二维dp,第一层遍历了那么第二层就不能遍历了
错误代码:

vector<int> levelOrder(TreeNode* root) {
	vector <int> ret;
	if (!root) {
		return ret;
	}
	queue <TreeNode*> q;
	q.push(root);
	while (!q.empty()) {
		int currentLevelSize = q.size();
		int x = 0;
		for (int i = 1; i <= currentLevelSize; ++i) {
			auto node = q.front(); q.pop();
			x+=node->val;
			if (node->left) q.push(node->left);
			if (node->right) q.push(node->right);
		}
		ret.push_back(x);
	}
	return ret;
}
int rob(TreeNode* root) {
	if (!root) return 0;
	vector<int> nums= levelOrder(root);
	int n = nums.size();
	if (n == 1) return nums[0];
	vector<vector<int>>dp(n, vector<int>(2, 0));//[i][0]表示第i个不偷,[i][1]表示第i个偷
	dp[0][0] = 0;
	dp[0][1] = nums[0];
	dp[1][0] = dp[0][1];
	dp[1][1] = nums[1];
	if (n == 2) return max(dp[1][0], dp[1][1]);
	for (int i = 2; i < n; i++) {
		dp[i][0] = max(dp[i - 1][1], dp[i - 1][0]);
		dp[i][1] = nums[i] + dp[i - 1][0];
	}
	return max(dp[n - 1][0], dp[n - 1][1]);
}

然后一想想好像不对劲,反例很好举:
请添加图片描述
比如这种情况,上面的运行结果是6,正确答案应该是7

正确思路: 递归后序遍历+自下而上动态规划+哈希表存储
用哈希表f存储选这个结点则该结点包括其子树能偷到的最大金额,g存储不偷这个结点则其俩子树能偷到的最大金额
则f[root] = root->val + g[root->left] + g[root->right];
g[root] = max(g[root->right], f[root->right]) + max(g[root->left], f[root->left]);
自下而上 最后返回max(f[root], g[root])即可
代码:

unordered_map<TreeNode*,int> f, g;//

void dfs(TreeNode* root) {//用递归形式后序遍历,自下而上动态规划
	if (!root) return;//空结点
	dfs(root->left);
	dfs(root->right);
	f[root] = root->val + g[root->left] + g[root->right];//选了这个结点,则其俩个子结点都不能选了,所以都是g,若其子节点不存在,map返回的是0
	g[root] = max(g[root->right], f[root->right]) + max(g[root->left], f[root->left]);//不选这个结点,则左右结点均可选或不选,左右各自取最大就行
}
int rob(TreeNode* root) {
	dfs(root);
	return max(f[root], g[root]);
}

时间空间均为O(n),其中空间还可以优化,因为父结点只会用到子结点的f和g,可以让dfs返回一个struct包含子结点的f和g,但是因为递归调用栈的原因,空间还是O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值