打家劫舍ⅠⅡⅢ(dp+记忆化搜索)

在这里插入图片描述

class Solution {
public:
	int rob(vector<int>& nums) {
		//dp[i]表示0~i个房间能够偷窃到的最高金额
		//dp[i] = max(dp[i-2]+nums[i],dp[i-1]);
		int len = nums.size();
		if (len == 0)return 0;
		if (len == 1)return nums[0];
		vector<int>dp(len, 0);
		dp[0] = nums[0];
		dp[1] = max(nums[0], nums[1]);
		for (int i = 2; i < len; i++)
		{
			dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);

		}
		return dp[len - 1];

	}
};

还可以把空间复杂度降到O(1):

class Solution {
public:
	int rob(vector<int>& nums) {
		int n = nums.size();
		if (!n) return 0;
		if (n == 1) return nums[0];
		if (n == 2) return max(nums[0], nums[1]);
		int tmp, pre = 0, cur = 0;
		for (int i = 0; i < n; i++) {
			tmp = cur;
			cur = max(pre + nums[i], cur);
			pre = tmp;
		}
	
		return cur;
	}
};

在这里插入图片描述
首先,首、尾房间不能同时被抢,那么只可能有两种不同情况:要么最后一间不抢;要么第一间不抢。

class Solution {
public:
    int rob(vector<int>& nums) {
		int len = nums.size();
		if (len==0)
			return 0;
		if (len == 1)
			return nums[0];
		vector<int> dp1(len, 0);//dp1[i]表示0~i个房间能够偷窃到的最高金额(不抢第一个)
		vector<int> dp2(len, 0);//dp2[i]表示0~i个房间能够偷窃到的最高金额(不抢最后一个)
		dp1[0] = 0, dp1[1] = nums[1], dp2[0] = nums[0], dp2[1] = max(nums[0], nums[1]);
		for (int i = 2; i < len; ++i) {
			dp1[i] = max(dp1[i - 1], nums[i] + dp1[i - 2]);
		}

		for (int i = 2; i < len - 1; ++i) {
			dp2[i] = max(dp2[i - 1], nums[i] + dp2[i - 2]);
		}

		return max(dp1[len - 1], dp2[len - 2]);
	}
};

还可以把空间复杂度降到O(1):

class Solution {
public:
	int rob(vector<int>& nums) {
		int n = nums.size();
		if (!n) return 0;
		if (n == 1) return nums[0];
		if (n == 2) return max(nums[0], nums[1]);
		int tmp, pre = 0, cur = 0;
		for (int i = 1; i < n; i++) {
			tmp = cur;
			cur = max(pre + nums[i], cur);
			pre = tmp;
		}
		int res1 = cur;
		pre = 0;
		cur = 0;
		for (int i = 0; i < n - 1; i++) {
			tmp = cur;
			cur = max(pre + nums[i], cur);
			pre = tmp;
		}
		int res2 = cur;
		return max(res1, res2);
	}
};

在这里插入图片描述
思路1:

class Solution {
	//当前结点选择不偷: 当前结点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
	//当前结点选择偷: 当前结点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前结点的钱数

public:
	
	int rob(TreeNode* root) {
		if (root == NULL)return 0;
		return DFS(root);
	}
	int DFS(TreeNode* root)
	{
		if (root == NULL)return 0;
		
		// 抢,然后去下下家
		int do_it = root->val
			+ (root->left == NULL ?
				0 : DFS(root->left->left) + DFS(root->left->right))
			+ (root->right == NULL ?
				0 : DFS(root->right->left) + DFS(root->right->right));
		// 不抢,然后去下家
		int not_do = DFS(root->left) + DFS(root->right);

		int res = max(do_it, not_do);
		return res;
	}
};

发现思路1并不能AC,这是因为递归会导致重复搜索最终超时,这其实还是相当于递归,只是用到了动态规划的思想,因为没办法从最小的子问题来求解。

改进思路:记忆化搜索:创建map<TreeNode*,int>来存储子问题的解。

class Solution {
public:
	map<TreeNode*, int> memo;
	int rob(TreeNode* root) {
		if (root == NULL)return 0;
		return DFS(root);
	}
	int DFS(TreeNode* root)
	{
		if (root == NULL)return 0;
		// 利用备忘录消除重叠子问题
		if (memo.count(root))
			return memo.find(root)->second;
		// 抢,然后去下下家
		int do_it = root->val
			+ (root->left == NULL ?
				0 : DFS(root->left->left) + DFS(root->left->right))
			+ (root->right == NULL ?
				0 : DFS(root->right->left) + DFS(root->right->right));
		// 不抢,然后去下家
		int not_do = DFS(root->left) + DFS(root->right);

		int res = max(do_it, not_do);
		memo.insert(make_pair(root, res));
		return res;
	}
};

这样就可以AC了!

发布了212 篇原创文章 · 获赞 4 · 访问量 8783

猜你喜欢

转载自blog.csdn.net/ShenHang_/article/details/104824471