题目描述

题目解答
对于这道题,我们可以把每一层看成一个数组,核心思路是一行一行地推:先把第一行 1 当作地基塞进去,然后从第 1 行开始往右推导每一行,每一行的新元素都只依赖上一行,这就是典型的"状态转移"------上一行是状态,新一行是结果。推新行时按 j 从左到右遍历,用三个分支判断当前位置怎么算:开头 j=0 时上一行没有左邻居,直接取 prelist.get(0)(恒为 1);结尾 j==prelist.size() 时已经走到上一行最右下标的下一位,没有右邻居可加,取 prelist.get(j-1)(也是 1);中间就是两个肩膀之和 prelist.get(j) + prelist.get(j-1)。
java
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> list = new ArrayList<>();
// 地基:第 0 行就是 [1]
List<Integer> inner = new ArrayList<>();
inner.add(1);
list.add(inner);
// 从第 1 行开始往后逐行推,每一行只依赖上一行
for (int i = 1; i < numRows; i++) {
List<Integer> prelist = list.get(i - 1);
List<Integer> curlist = new ArrayList<>();
// 当前行有 i+1 个元素,下标 j 从 0 到 i
for (int j = 0; j <= i; j++) {
// 左边界:上一行没有 j-1 这个位置,直接拿 prelist.get(0) = 1
if (j - 1 < 0) {
curlist.add(prelist.get(j));
continue;
}
// 右边界:j 已经超出上一行最后一个下标,没有右邻居,拿 prelist 末尾 = 1
if (j == prelist.size()) {
curlist.add(prelist.get(j - 1));
continue;
}
// 中间位置:上一行左右两肩之和
curlist.add(prelist.get(j) + prelist.get(j - 1));
}
list.add(curlist);
}
return list;
}
}
2.题目描述

题目解答
1.递归+记录数组
对于这道题目有一个很重要的点就是:对于当前的每一户来说,只有两个状态:偷或者不偷,所以我们可以将这两种状态下的值进行比较,哪个情况下的值最大,那个就是当前的最优解,首先,我们可以使用递归的方式,由上到下进行问题的拆分。为了防止递归时对同一个位置进行重复计算,我们可以建立一个数组来保存每一个位置的最优解,当发现此处有值的时候,我们就可以直接拿出来使用,而不是进行第二次递归。
java
class Solution {
int[] memo;
public int rob(int[] nums) {
memo = new int[nums.length];
Arrays.fill(memo, -1);
return dfs(nums, nums.length - 1);
}
private int dfs(int[] nums, int i) {
if (i < 0) return 0;
if (memo[i] != -1) return memo[i]; // 算过直接拿
if (i == 0) return memo[0] = nums[0];
return memo[i] = Math.max(dfs(nums, i - 1), nums[i] + dfs(nums, i - 2));
}
2.设置状态
既然我们知道了核心思想,所以我们可以直接放弃递归,只用两个值来代表前面的第一个和第二个的最优解。
java
// dp[i] 表示前 i+1 间房子(0..i)能偷到的最大金额;本题只跟 dp[i-1]、dp[i-2] 有关,所以用滚动变量把空间压到 O(1)
class Solution {
int[] memo; // 这个字段没用上,多余的,可以删
public int rob(int[] nums) {
// 没有房子可偷 → 0
if(nums.length==0) return 0;
// 只有一间 → 不偷白不偷
if(nums.length==1) return nums[0];
int prev2=nums[0]; // dp[i-2]:只考虑前 1 间时的最大金额
int prev1=Math.max(prev2,nums[1]); // dp[i-1]:考虑前 2 间时,挑钱多的那间偷
// 从第 3 间开始逐间决策:偷 i(则 i-1 不能偷,加上 prev2)还是不偷 i(保持 prev1)
for(int i = 2;i<nums.length;i++){
int cur = Math.max(nums[i]+prev2,prev1);
prev2=prev1; // 滚动:旧 dp[i-1] 变成新的 dp[i-2]
prev1 = cur; // 当前结果变成新的 dp[i-1]
}
return prev1; // 最后一个滚动变量就是 dp[n-1],即答案
}
}