就是一个简单的动态规划,我一开始想到直接递归,但是后来发现时间复杂度高的吓人,是指数级别的,后来看了官方的解答,适当的开辟空间确实对程序的优化效果非常明显。如果有不足之处希望大家在留言区指正。
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 400
看到题目给出的主要限制是房间之间的联系,可以很快的想到是用递归去拆分问题解决,最终可以将问题化为只有一间房屋或两间房屋的情况。
1.我一开始想到的递推式:
f(n)={
nums[0] (n=1)//只剩一间房屋没有考虑的情况
MAX(nums[0], nums[1]) n=2//只剩两间房屋没有考虑的情况
MAX(nums[n]+f(n-2) , f(n-1)) n>=3 //前面是偷这件房屋的情况,后面是不偷的情况
}
时间复杂度分析:
复杂度类似于斐波拉契数列,可由递归树知道f(n)=f(n+1)+f(n+2)(n是递归的层数);
最后计算出复杂度为O(2^n),这个复杂度太可怕了。不过可以记录每次递归的值,可以减小时间复杂度到O(n),但是我懒得去搞了。
2.官方解答给出的公式:
引入数组dp[numsSize][2],后面那个2用于标记房屋的状态,1表示偷窃0表示不偷窃,初始化dp[0][0]=0,dp[0][1]=nums[0]。应为标记了状态,所以用递归不方便,官方用for循环。
对每一个房屋的不同状态进行赋值,最后一个房屋的两种状态下的最大值就是目标。
dp[i][0]=MAX(dp[i-1][1],dp[i-1][0]);//当前房屋如果不偷窃,则有对前一种房屋有两种选择。
dp[i][1]=dp[i-1][0]+nums[i-1];//当前房屋偷窃,则前一个房屋一定不能偷窃。
int rob(int* nums, int numsSize) {
int max;
if (numsSize == 0)
return 0;
if (numsSize == 1)
return nums[0];
max = getmax(nums, 0, numsSize - 1);
return max;
}
int getmax(int* nums, int head, int tail) {
int data1, data2;
if (head + 1 == tail) {
data1 = nums[head];
data2 = nums[tail];
return data1 > data2 ? data1 : data2;
}
else if (head == tail) {
return nums[head];
}
data1 = getmax(nums, head + 2, tail) + nums[head];
data2 = getmax(nums, head + 1, tail);
return data1>data2?data1:data2;
}
时间复杂度:O(2^n)
int rob(int* nums, int numsSize){
int max,data;
if(numsSize==0)
return 0;
int dp[numsSize][2];
dp[0][0]=0;
dp[0][1]=nums[0];
for(int i=1;i<numsSize;i++){
dp[i][0]=dp[i-1][0]>dp[i-1][1]?dp[i-1][0]:dp[i-1][1];
dp[i][1]=dp[i-1][0]+nums[i];
}
data=dp[numsSize-1][0]>dp[numsSize-1][1]?dp[numsSize-1][0]:dp[numsSize-1][1];
return data;
}
时间复杂度O(n);