题目描述
有一个长度为 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
示例 3:
输入:steps = 4, arrLen = 2
输出:8
注意:
1 <= steps <= 500
1 <= arrLen <= 10^6
思路
- 这个题目一眼望去动态规划~
- 创建一个二维数组dp,其中dp[ i ] [ j ] 表示第 i 步处于位置 j 的方案总数。
非常容易发现状态转移公式为:dp[ i ] [ j ] = dp[ i -1] [ j-1 ] + dp[ i -1] [ j ] + dp[ i-1 ] [ j+1 ],以及边界条件dp[ 0 ] [ 0 ] = 1 - 但是按照上面的说法建数组,内存开销太大了,因为数组的第二维arrLen是10^6数量级。注意到,我们要求进行steps步操作后,位于0位置的,对于位置下标大于steps的,就算每一步让它往左移动,都无法在最后到达0位置,所以过大的下标位置不用考虑。设置dp数组的第二维为最大下标为 min (steps , arrLen-1 )。
- 然后按照上面说的迭代求解,最后返回 dp[steps][0]即可~
- 注意到在第 i 步时,我们只用到了第 i-1步的结果,所以空间复杂度可以进一步压缩。只用两个一维数组 dp 和 newDp即可实现上面的操作~
- 时间复杂度为 O( steps×min(arrLen,steps) )
- 空间复杂度为 O(min(arrLen,steps))
代码
class Solution {
public int numWays(int steps, int arrLen) {
int MM = 1000000007;
int len = Math.min(steps, arrLen-1);
int []dp = new int [len+1];
dp[0] = 1;
for(int i = 1;i <= steps;i++) {
int []newDp = new int [len+1];
for(int j = 0;j <= len;j++) {
newDp[j] = dp[j];
if(j > 0)
newDp[j] = (newDp[j] + dp[j-1])%MM;
if(j < len)
newDp[j] = (newDp[j] + dp[j+1])%MM;
}
dp = newDp;
}
return dp[0];
}
}