118. 杨辉三角
动态规划:
java
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> ans = new ArrayList<>();
//答案里必有[1]
ans.add(new ArrayList<>(List.of(1)));
//从第2行开始
for(int i = 1;i<numRows;i++){
List<Integer> part = new ArrayList<>();
//第一个数字必是1
part.add(1);
//除第一个和最后一个数字外,每个数字等于上一行下标-1和下标的和
for(int j = 1;j<i;j++){
part.add(ans.get(i-1).get(j-1) + ans.get(i-1).get(j));
}
//最后一个数字也是1
part.add(1);
ans.add(part);
}
return ans;
}
}
时间复杂度:O(N²),N 是行数
空间复杂度:O(N),不算返回结果
思路:找到每一行除1外数字和上一行的联系
198. 打家劫舍
动态规划:
java
class Solution {
public int rob(int[] nums) {
int N = nums.length;
//存储前k个房子里能偷到的最多的钱
int[] dp = new int[N+1];
//定义子问题:子问题是计算前k个房间里能偷的最多的钱
//子问题的解法: f(k) = max(f(k-1),f(k-2)+nums[k-1])
//子问题的计算顺序,一般都是自下到上,所以需要0和1
dp[0] = 0;
dp[1] = nums[0];
for(int k = 2;k<=N;k++){
dp[k] = Math.max(dp[k-1],dp[k-2]+nums[k-1]);
}
return dp[N];
}
}
思路:
这是一道很典型的动态规划题。
动态规划的本质,就是把原问题拆成规模更小的子问题,
并把子问题的答案保存下来,后面需要的时候直接复用,
最终得到原问题的答案。
本题的原问题是:
求前 N 间房子中,最多能偷到多少钱。
那么可以定义子问题为:
求前 k 间房子中,最多能偷到多少钱。
用 dp[k] 表示前 k 间房子能够偷到的最大金额。
接下来需要找到子问题之间的关系。
对于前 k 间房子来说,最后一间房子有两种选择:
- 不偷第 k 间房子
如果不偷最后一间房子,那么问题就变成:
前 k - 1 间房子中最多能偷多少钱。
也就是:
dp[k - 1]
- 偷第 k 间房子
如果偷最后一间房子,由于相邻房子不能同时偷,
所以第 k - 1 间房子不能偷。
此时能偷到的钱就是:
前 k - 2 间房子的最大金额 + 第 k 间房子的钱
也就是:
dp[k - 2] + nums[k - 1]
注意,这里的 dp[k] 表示的是前 k 间房子,
而数组 nums 的下标是从 0 开始的,
所以第 k 间房子的钱是 nums[k - 1]。
因此状态转移方程为:
dp[k] = max(dp[k - 1], dp[k - 2] + nums[k - 1])
确定状态转移方程之后,再确定计算顺序。
动态规划大多数都是自底向上计算,
也就是先算小规模问题,再一步步推出大规模问题。
所以需要先确定初始状态:
dp[0] = 0
表示没有房子时,最多能偷 0 元。
dp[1] = nums[0]
表示只有一间房子时,只能偷这一间。
最后从 k = 2 开始,按照状态转移方程依次计算即可。
时间复杂度:O(N)
空间复杂度:O(N)