题目
思路
我们从最后一步开始考虑,最后一步需要到达0
, 则上一步必定到达1
或者0
,更一般地说如果第s
步到达了p
,那么第s-1
步必须到达p - 1
或者p
或者p + 1
。所以,如果我们能够分别知道第s - 1
步到达p
, p - 1
和p + 1
的方案数量,那么第s
步到达p
的方案数量就是第s - 1
步到达p
, p - 1
和p + 1
的方案数量的总和。
根据上述分析,我们需要从第0
步开始,求出第s (0 <= s <= steps)
步到达各个位置p
的方案数量,我们使用数组dp[s][p]
来表示这个数量。可以知道(如果p - 1
和p + 1
越界则不进行累加)
dp[s][p] = dp[s - 1][p - 1] + dp[s - 1][p] + dp[s - 1][p + 1]
对于step=0
的初始情况,有
dp[0][0] = 1
dp[0][p] = 0 (p != 0)
最终的答案即为dp[steps][0]
(第steps
步到达0
的方案数目)。
可以看到,我们在计算和使用dp
时仅使用了dp[s]
和dp[s-1]
(计算第s
步仅使用第s-1
步的结果),因此dp
数组的第一维度长度可以设置为2
而不是steps + 1
。这时候对于第s
步,在dp
数组中索引为index = (i + 2) % 2
, 第s-1
步在dp
数组中索引为index = (i + 1) % 2
。
另外,根据题目中每个步骤尽可以+1
或者-1
,并且起始位置为0
我们可以知道不考虑数组长度的情况下,最远的p
为steps - 1
, 同时考虑数组长度的限制,则有 0 <= p < min(steps, arrLen)
,因此,根据题目给的数据范围,p
最大值为499 (500 - 1)
,因此dp
数组的第二个维度最大长度500
,相比arrLen (10 ^ 6)
就很小了。
代码
class Solution {
public:
int numWays(int steps, int arrLen) {
return dp_solve(steps, arrLen);
}
// 递归搜索,会超时
int dfs(int steps, int end, int des) {
int total = 0;
if (steps == 0) {
if (des == 0) {
total += 1;
}
return total;
}
if (des > 0) {
total += find(steps - 1, end, des - 1);
}
if (des < end) {
total += find(steps - 1, end, des + 1);
}
total += find(steps - 1, end, des);
return total;
}
// 动态规划
int dp_solve(int step, int arrLen) {
long long dp[2][500];
int end = min(step, arrLen);
for(int i=0; i<end; i++) {
dp[0][i] = 0;
}
dp[0][0] = 1;
for(int i=1; i<=step; i++) {
int index = (i + 2) % 2;
int index_last = (i + 1) % 2;
for (int j=0; j<end; j++) {
long long total = 0;
if (j > 0) {
total += dp[index_last][j - 1];
}
if (j < end - 1) {
total += dp[index_last][j + 1];
}
total += dp[index_last][j];
dp[index][j] = total % (1000000007);
}
}
return dp[step % 2][0];
}
};
总结
本题中需要搜索的最长数组长度为right_end = min(step, arrLen)
,我在编写程序的过程中有个地方把 right_end
和step
弄混了,导致数组越界访问,但没有触发数组越界的保护机制,因此在运行时就出现答案时对时错的情况,花了不少时间才找到错误ಥ_ಥ.