题目描述【困难】
有一个长度为
arrLen
的数组,开始有一个指针在索引0
处。每一步操作中,你可以将指针向左或向右移动
1
步,或者停在原地(指针不能被移动到数组范围外)。给你两个整数
steps
和arrLen
,请你计算并返回:在恰好执行steps
次操作以后,指针仍然指向索引0
处的方案数。由于答案可能会很大,请返回方案数 模
10^9 + 7
后的结果。
示例 1:
输入:
steps = 3
,arrLen = 2
输出:4
解释:3
步后,总共有4
种不同的方法可以停在索引0
处。
向右,向左,不动
不动,向右,向左
向右,不动,向左
不动,不动,不动
示例 2:
输入:steps =
2
, arrLen =4
输出:2
解释:2
步后,总共有2
种不同的方法可以停在索引0
处。
向右,向左
不动,不动
示例 3:
输入:
steps = 4
,arrLen = 2
输出:8
提示:
1 <= steps <= 500
1 <= arrLen <= 10^6
题目地址:地址
初步分析
遇到需要多少步后得到多少种方案的,可以一律用动态规划解题
正式分析
既然基于动态规划解题,则我们分析其动态规划的状态转移方程:
我们以 dp[i][j]
为在第 i
步时,所处下标为 j
的所用 方案数
其中:
i
的范围值是0 ~ steps
j
的范围值,也就是0
到最远可到达的位置,即arrLen - 1
(数组边界) 与steps
(步数上限) 的最小值,即0 ~ Math.min(arrLen - 1, steps)
而 dp[i][j]
的状态来源为:
- 来自
j - 1
的位置:由左移动过来 - 来自
j + 1
的位置:由右移动过来 - 来自
j
的位置:不动
因此,状态转移方程即为:
dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1] + dp[i - 1][j]
接下来,我们分析边界值情况:
- 当
i = 0
时,下标j
一定在0
,因此此时的dp[0][0] = 1
因此当i = 0
时,下标j
肯定不能处于任何非0
的位置,所以dp[0][j] = 0
- 当
j >= 1
时,状态值来源肯定是可以来自于dp[i - 1][j - 1]
,也就是可以来自于由左边(j - 1)移动而来
- 当
j < Math.min(arrLen - 1, steps)
时,状态值来源可以来自于dp[i - 1][j + 1]
,即由右边(j + 1)移动而来
根据以上梳理,即可求得代码:
具体代码
初始方案
function numWays(steps: number, arrLen: number): number {
const MODULO = 1000000007;
const indexMax = Math.min(steps, arrLen - 1)
const dp: number[][] = new Array(steps + 1).fill(0).map(item => new Array(indexMax + 1).fill(0))
dp[0][0] = 1
for (let i = 1; i <= steps; i++) {
for (let j = 0; j <= indexMax; j++) {
dp[i][j] = dp[i - 1][j]
if (j >= 1) {
dp[i][j] = (dp[i][j] + dp[i - 1][j - 1]) % MODULO
}
if (j < indexMax) {
dp[i][j] = (dp[i][j] + dp[i - 1][j + 1]) % MODULO
}
}
}
return dp[steps][0]
};
TS AC,执行用时:144 ms
, 在所有 TypeScript
提交中击败了 100%
的用户
对上面的代码,我们分析后发现,其实 最远走到的下标
应该是 Math.floor(steps / 2)
与 arrLen - 1
中最小值,因为如果超出了 Math.floor(steps / 2)
,则 j
永远都不可能回到 0
上,至于为什么用 Math.floor
,可以思考一下为什么
另外,我们可以发现,dp
一直依赖于上一行的状态值,即 i - 1
,所以我们可以把 dp
简化成一维数组:
优化后
function numWays(steps: number, arrLen: number): number {
const MODULO = 1000000007;
const indexMax = Math.min(Math.floor(steps / 2), arrLen - 1)
let dp: number[] = new Array(indexMax + 1).fill(0)
dp[0] = 1
for (let i = 1; i <= steps; i++) {
const dpNext = new Array(indexMax + 1).fill(0)
for (let j = 0; j <= indexMax; j++) {
dpNext[j] = dp[j]
if (j >= 1) {
dpNext[j] = (dpNext[j] + dp[j - 1]) % MODULO
}
if (j < indexMax) {
dpNext[j] = (dpNext[j] + dp[j + 1]) % MODULO
}
}
dp = dpNext
}
return dp[0]
};
TS AC,执行用时:104 ms
, 在所有 TypeScript
提交中击败了 100%
的用户
最后
如果我在哪里写的有问题,欢迎指出,共同进步,谢谢阅读~