题目分析
这个题目实际上是在模拟弹珠通过版面的过程,使用动态规划来计算从最上方到达最底部时,能获得的最高分数。由于我们可以自由选择起始位置,同时弹珠一定会落到底部,因此我们可以从最底层逐渐向顶层更新 dp 数组求解。题目中的版面可以通过一个二维数组表示,其中每个格子有三种状态:钉子(-1),得分点(非负整数),空白(0)。我们的目标是从版面的最上面任意一列放置弹珠,计算出弹珠经过每个位置时能获得的最大分数。
解题思路
1. 状态定义: 我们定义一个 dp 数组,其中 dp[i][j] 代表从第 i 行,第 j 列的位置开始,能获得的最大分数。我们从最底部的行开始往上填充这个数组。然后,最后计算第一行的最大值,即为我们能获得的最高分数。
2. 边界条件: 最底部的行 dp[n-1][j] 直接根据当前位置的状态确定:
- 如果该位置是钉子(
array[n-1][j] == -1),得分为0,因为无法从钉子上弹射。 - 如果该位置是得分点(
array[n-1][j] >= 0),那么得分就是该得分点的分数。 - 如果该位置为空白(
array[n-1][j] == 0),得分为0,因为弹珠会直接往下掉。
3. 状态转移: 对于非底部的行,我们需要考虑每个位置的状态:
-
如果当前位置是钉子(
array[i][j] == -1) ,弹珠可以向左下或右下弹射,依赖于上一行左下或右下的位置:- 如果当前列
j不是最左列且不是最右列,弹珠可以选择从左下(dp[i + 1][j - 1])或右下(dp[i + 1][j + 1])弹射。 - 如果是最左列(
j == 0),弹珠只能从右下(dp[i + 1][j + 1])弹射。 - 如果是最右列(
j == m - 1),弹珠只能从左下(dp[i + 1][j - 1])弹射。
- 如果当前列
-
如果当前位置是空白(
array[i][j] == 0) ,弹珠会直接从上方的位置落下,即继承上一行同一列的分数dp[i][j] = dp[i+1][j]。 -
如果当前位置是得分点(
array[i][j] > 0) ,弹珠会从上方的位置直接下落,且获得当前位置的得分。因此,dp[i][j] = array[i][j] + dp[i+1][j]。
4. 结果计算: 完成 dp 数组的填充后,第一行中的最大值即为从任意列开始,弹珠能够获得的最大分数。
代码解析
public static int solution(int n, int m, int[][] array) {
// 初始化DP数组
int[][] dp = new int[n][m];
// 边界条件:最底层的行
for (int j = 0; j < m; j++) {
dp[n-1][j] = array[n-1][j] != -1 ? array[n-1][j] : 0;
}
// 状态转移
for (int i = n-2; i >= 0; i--) {
for (int j = 0; j < m; j++) {
if (array[i][j] == -1) {
// 钉子:考虑向左下和向右下弹射
if (j > 0 && j < m - 1) {
dp[i][j] = Math.max(dp[i + 1][j - 1], dp[i + 1][j + 1]);
} else if (j == 0) {
dp[i][j] = dp[i + 1][j + 1];
} else if (j == m - 1) {
dp[i][j] = dp[i + 1][j - 1];
}
} else {
// 得分点或空白:直接从上方继承分数
dp[i][j] = array[i][j] + dp[i+1][j];
}
}
}
// 计算最大分数
int maxScore = 0;
for (int j = 0; j < m; j++) {
maxScore = Math.max(maxScore, dp[0][j]);
}
return maxScore;
}
代码解析
- 初始化
dp数组: 在最底行(n-1)上,我们直接根据array[n-1][j]的值来初始化dp数组。如果该位置是钉子(-1),那么dp[n-1][j]就为0;否则,得分为该位置的得分。 - 动态规划转移: 从倒数第二行开始,逐行计算每个位置的最大得分。若当前格子是钉子(-1),就考虑从左下或右下位置传递得分;若当前格子是空白(0)或得分点(非负数),则直接从下方位置继承得分。
- 计算结果: 最后,我们遍历第一行的每个位置,找到其中的最大值,这就是我们最终的结果。
时间复杂度分析
- 初始化
dp数组需要 O(M) 时间,其中 M 是列数。 - 动态规划填充
dp数组的时间复杂度是 O(N * M),其中 N 是行数,M 是列数。 - 最终计算最大值的时间复杂度是 O(M)。
因此,总的时间复杂度为 O(N * M),这对于最大输入大小(N, M <= 1000)是可以接受的。
空间复杂度分析
- 我们使用了一个
dp数组,大小为N * M,因此空间复杂度为 O(N * M)。