你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。
示例 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 。
从最简单的思路开始,一步一步优化. 首先这个是一个动态规划的问题 , 把大问题拆成可以重复的小问题 ,在找到边界条件 ,就可以完成大半了, 如果没有理解动态规划的思想,还是要仔细琢磨琢磨最简单的算法.
既然是开始偷,那么这个房间就有偷或者不偷2种选择,如果偷的话,下次的可供选择的就是index+2,如果不偷的话,下次的可以选择的就是index+1, 比较 rooms[index]+rob(index+2) 与 rob[index+1]中的较大值 就可以了.
至于边界条件,如果是最后一个房间,那么不用想了,直接偷; 如果还有2个房间,那就找出这2个中的较大者即可.
#import <Foundation/Foundation.h>
int rooms[] = {1,4,13,45,25,12};
int nums = 6;
int robAction(int index) {
// 到了最后一个,直接选择偷
if (index == nums-1) {
return rooms[index];
}
// 到了倒数第二个,选择最后2个中的较大者
if (index == nums-2) {
return MAX(rooms[nums-2], rooms[nums-1]);
}
return MAX(rooms[index] + robAction(index+2), robAction(index+1));
}
int main(int argc, const char * argv[]) {
int result = robAction(0);
NSLog(@"%@",@(result));
return 0;
}
上一个就可以解决问题了,但是放到LeetCode上提示超时了, 因为没有保存子问题的结果,每次都会重新计算.所以这一次加上缓存. 用cache存储计算的结果,避免重复计算.
#import <Foundation/Foundation.h>
int nums = 6;
int rooms[] = {1,4,13,45,25,12};
int cache[100] = {};
// 正序偷
int robAction(int index) {
if (index == nums-1) {
cache[index] = rooms[index];
return cache[index];
}
if (index == nums-2) {
cache[index] = MAX(rooms[index], rooms[index+1]);
return cache[index];
}
if (cache[index] != 0) {
return cache[index];
}
cache[index] = MAX(rooms[index] + robAction(index+2), robAction(index+1));
return cache[index];
}
int main(int argc, const char * argv[]) {
int result = robAction(0);
NSLog(@"---%@",@(result));
return 0;
}
理论上到这一步就是可以了, 但是看了别人的算法, 有非递归形式的 , 用一个数组存贮结果 , 这个数组存的截止第i个屋子的最大收益, 比如到第5个屋子,有 偷/不偷 2种选择,
选择偷的话,(截止到第3个屋子结果)+(第5个屋子的结果)=偷选择的最大收益,
选择不偷的话, (截止第4个屋子的结果)=不偷选择最大收益,
比较这个2个最大收益的较大者放入到数组第i个元素中,继续向下.
#import <Foundation/Foundation.h>
#define nums 18
int rooms[] = {1,4,13,45,25,12, 34,22,45,25,77,35, 45,42,56,77,23,12, };
// for循环,非递归
int robAction(int index) {
// 存放偷到第i个屋子的最大收益
int result[nums] = {0};
// 初始条件,如果屋子的数量小于2时的结果
result[0] = rooms[0];
result[1] = MAX(rooms[0], rooms[1]);
for (int i=2;i<nums ; i++) {
// 到了第i个屋子,有2种选择,取最大值存入
// 偷:用前i-2的结果加上这个屋子的金额
// 不偷:前i-1个屋子的金额
result[i] = MAX(result[i-2]+rooms[i], result[i-1]);
}
return result[nums-1];
}
int main(int argc, const char * argv[]) {
int result = robAction(0);
NSLog(@"---%@",@(result));
return 0;
}
最后放下LeetCode上的解法,额~是对非递归算法的优化吧,不需要数组了,用局部变量完成的存储. 是节省内存了,但是有点不好理解了
#import <Foundation/Foundation.h>
#define nums 18
int rooms[] = {1,4,13,45,25,12, 34,22,45,25,77,35, 45,42,56,77,23,12, };
// for循环,非递归
int robAction(int index) {
int prevMax = 0;
int currMax = 0;
for (int i=0;i<nums ; i++) {
int temp = currMax;
currMax = MAX(prevMax + rooms[i], currMax);
prevMax = temp;
}
return currMax;
}
int main(int argc, const char * argv[]) {
int result = robAction(0);
NSLog(@"---%@",@(result));
return 0;
}