关于动态规划理解可以参考 https://www.sohu.com/a/149075950_684445
70. 爬楼梯
1. 题目
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
2. 代码
class Solution {
public:
int climbStairs(int n) {
if (n <= 0) {return 0;}
if (n == 1) {return 1;}
if (n == 2) {return 2;}
int f1 = 1;
int f2 = 2;
int tmp = 0;
for (int i = 3; i <= n; i++) {
tmp = f1 + f2;
f1 = f2;
f2 = tmp;
}
return tmp;
}
};
3. 题解
- 假设有5阶台阶:
假设只差一步就走到5阶, 有两种可能性,现在在第3个台阶,再走两步; 现在在第4个台阶,再走一步
假设走到第3个台阶有x种方法, 记作f(3) = x;走到第4个台阶有y种方法,记作f(4) = y; 那么走到第5个台阶就是f(5) = x+y种方法, 那么f(3), f(4)就是【最优子结构】 - 当只有1个台阶,只有1种方法; 只有2个台阶,只有两种方法,f(1), f(2)就是问题的【边界】
- 由1可得n个台阶 f(n) = f(n-2) + f(n-1); 就是问题的【状态转移方程】
- 此时可以用递归实现:
int climbStairs(int n) {
if (n <= 0) {return 0;}
if (n == 1) {return 1;}
if (n == 2) {return 2;}
return climbStairs(n-2) + climbStairs(n-1);
}
但是此时时间复杂度O(2^n),太过于耗时,而且做了很多重复性的计算
- 在此基础上,可以使用map存储每一个台阶的解答,重复性的不去递归
map<int, int> map;
int climbStairs(int n) {
if (n <= 0) {return 0;}
if (n == 1) {return 1;}
if (n == 2) {return 2;}
auto it = map.find(n);
if (it != map.end()) {
return it->second;
} else {
int ret = climbStairs(n-2) + climbStairs(n-1);
map[n] = ret;
return ret;
}
}
此时时间复杂度和空间复杂度都是O(n)
- 把思路逆转一下,为什么非要自顶向下计算f(n-2) + f(n-1), 不能自底向上计算呢,先计算f(3) = f(1) + f(2), f(4) = f(2) + f(3), … f(n) = f(n-2) + f(n-1);
采用示例代码的动态规划算法,空间复杂度O(1)
198. 打家劫舍
1. 题目
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
2. 代码
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if (n <= 0) { return 0; }
if (n == 1) { return nums[0]; }
if (n == 2) { return max(nums[0], nums[1]); }
int f1 = nums[0];
int f2 = max(nums[0], nums[1]);
int tmp = 0;
for (int i = 3; i <= n; i++) {
tmp = f2;
f2 = max(f1+nums[i-1], f2);
f1 = tmp;
}
return f2;
}
};
3. 题解
- 假设有4间房子, 第4间房子有两种情况,偷还是不偷,
(1)偷的话, 为了不触发警报, 一定会偷第2间房子, 那总金额就是f(2) + 第4间房子的金额(nums[3])
(2)不偷的话, 那么一定会偷第3间房子, 总金额就是f(3) - 【最优子结构】就是f(2)+nums[3], f(3)
【边界】 只有一个房子f(1) = nums[0]; 只有两个房子, f(2) = max(nums[0], nums[1]);
【状态转移方程】 f(n) = max(f(n-2)+nums[n-1], f(n-1)); - 自底向上计算, f(3)=max(f(1)+nums[2], f(2)), f(4)=max(f(2)+nums[3], f(3))… max(f(n-2)+nums[n-1], f(n-1));
f1 表示 f(n-2)
f2 表示 f(n-1)