LeetCode——198. House Robber

本文探讨了一个经典的动态规划问题——房屋抢劫问题。在一个街区中,每栋房子都有一定的金额,但相邻的房子装有相互连通的防盗系统,若连续两栋房子被盗窃,则会触发报警。文章详细解析了如何通过动态规划算法,找到盗窃方案以获得最大收益,避免触发报警。
摘要由CSDN通过智能技术生成

1.题目大意

假如你是一名小偷,现在准备去偷一条街道上的所有房子里的钱。为了防止偷盗,房子间有防盗装置:只要你偷盗了邻近的两间房子就会惊动警察。请问,如何偷盗才有最大收获?

输入代表房子钱财的数组,输出最大收获,如输入[1, 2, 3, 1]就应该输出4。

2.解题思路

以动态规划的思路来解决这个问题:可以将如何从[n1, n2, …, nk]这里面选择互不相邻的元素有最大的和的问题分为小问题,即在选了nk的情况下与不选nk的情况下选取最优值。如果C(i)表示从[1, i]区间的下标中选择互不相邻的元素的最大和,那么C(i) = max(C(i - 2) + array(i), C(i - 1)),即将从[1, i]中挑选元素的问题变为从选了i元素并从[1, i - 2]区间中继续挑选,还是不选i元素而从[i, i -1]区间挑选。

class Solution {
public:
	int rob(vector<int>& nums) {
		return sub(nums, nums.size() - 1);
	}

	int sub(vector<int>& nums, int index) {
		return index < 0 ? 0 : max(sub(nums, index - 2) + nums.at(index), sub(nums, index - 1));
	}
};

上面的这个方法虽然可行,但是如果a表示长度为i的数组的递归次数,b表示长度为i - 1的数组的递归次数,那么长度为i + 1的数组的递归次数就等于1 + a + b,即O(n) = O(n!)。里面有太多的重复计算和递归了,这时应该用memo数组将那些已经计算过了的值存储起来。注意,memo数组里面下标为i的元素就表示nums数组中[0, i]区间的元素中挑选的元素的和的最大值

class Solution {
private:
	int* memo;
public:
	int rob(vector<int>& nums) {
		int size = nums.size();
		memo = new int[size];
		for (int i = 0; i < size; i = i + 1)
			memo[i] = -1;
		int result = rob(nums, size - 1);
		delete [] memo;
		return result;
	}
	int rob(vector<int>& nums, int index) {
		if (index < 0)
			return 0;
		if (memo[index] >= 0)
			return memo[index];
		memo[index] = max(rob(nums, index - 1), rob(nums, index - 2) + nums.at(index));
		return memo[index];
	}
};

但是,递归计算其实还是太消耗性能了,可以考虑用for循环来取代递归,尤其是当memo中i - 1下标的元素总比i下标的元素早计算出来。

class Solution {
public:
	int rob(vector<int>& nums) {
		int size = nums.size();
		if (size == 0)
			return 0;
		int memo[size + 1];
		memo[0] = 0;
		memo[1] = nums.at(0);
		for (int i = 1; i < size; i = i + 1) {
			memo[i + 1] = max(memo[i], memo[i - 1] + nums.at(i));
		}
		return memo[size];
	}
};

还能不能优化呢,其实还是可以的,只不过这会不是时间复杂度了,而是空间复杂度。从上面的代码可以看出,其实一直是用memo[i]以及memo[i - 1]元素的值来求导memo[i + 1],前面的比i - 1小的数没有再使用过,所以只要用两个数来取代数组就可以了。
所以,最终版代码如下:

class Solution {
public:
	int rob(vector<int>& nums) {
		int size = nums.size();
		if (size == 0)
			return 0;
		int num1 = 0;
		int num2 = 0;
		for (int i = 0; i < size; i = i + 1) {
			int temp = num1;
			num1 = max(nums.at(i) + num2, num1);
			num2 = temp;
		}
		return num1;
	}
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值