LeetCode 1269. 停在原地的方案(数滚动数组优化)
一、题目描述
题面
有一个长度为 arrLen 的数组,开始有一个指针在索引 0 处。
每一步操作中,你可以将指针向左或向右移动 1 步,或者停在原地(指针不能被移动到数组范围外)。
给你两个整数 steps 和 arrLen ,请你计算并返回:在恰好执行 steps 次操作以后,指针仍然指向索引 0 处的方案数。
由于答案可能会很大,请返回方案数 模 10^9 + 7 后的结果。
样例
输入:steps = 3, arrLen = 2
输出:4
解释:3 步后,总共有 4 种不同的方法可以停在索引 0 处。
向右,向左,不动
不动,向右,向左
向右,不动,向左
不动,不动,不动
输入:steps = 2, arrLen = 4
输出:2
解释:2 步后,总共有 2 种不同的方法可以停在索引 0 处。
向右,向左
不动,不动
输入:steps = 4, arrLen = 2
输出:8
数据范围
1 <= steps <= 500
1 <= arrLen <= 10^6
二、解题思路
1、动态规划
令dp[i][j]表示到第i步在j位置上的情况数,dp[i][j]的情况可以从dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1]转移过来,注意边界。
- dp[i][j]=dp[i-1][j-1]+dp[i-1][j]+dp[i-1][j+1]
因为判断分支会减慢速度,推荐这种写法:
for(int i=1;i<=steps;i++)
{
dp[i][0]=(dp[i-1][0]+dp[i-1][1])%M;
dp[i][arrLen-1]=(dp[i-1][arrLen-1]+dp[i-1][arrLen-2])%M;
//将边界放在外层循环单独处理,而不是在内层循环用if判断处理。
for(int j=arrLen-2;j>0;j--)
{
dp[i][j]=((dp[i-1][j]+dp[i-1][j-1])%M+dp[i-1][j+1])%M;
}
}
2、时间常数优化
同时观察到,由于arrLen如果大于2·steps,那么大于2·steps的位置根本无法达到,所以可以缩小arrLen范围。
arrLen=min(arrLen,(steps>>1)+1);
因为1e9+7的三倍会大于int的范围,用long long会变慢,所以dp[i][j]=dp[i-1][j-1]+dp[i-1][j]+dp[i-1][j+1],所以在前两项相加时可以先膜一下:
dp[i][j]=((dp[i-1][j]+dp[i-1][j-1])%M+dp[i-1][j+1])%M;
3、空间常数优化
注意到按steps分层时第i层只取决于第i-1层,可以用滚动数组优化:
for(int i=1;i<=steps;i++)
{
dp[i&1][0]=(dp[~i&1][0]+dp[~i&1][1])%M;
dp[i&1][arrLen-1]=(dp[~i&1][arrLen-1]+dp[~i&1][arrLen-2])%M;
for(int j=arrLen-2;j>0;j--)
{
dp[i&1][j]=((dp[~i&1][j]+dp[~i&1][j-1])%M+dp[~i&1][j+1])%M;
}
}
像这样用位运算进行滚动数组,而不用if分支判断,会更快。
三、完整代码
class Solution
{
public:
int numWays(int steps, int arrLen)
{
int M=1e9+7;
arrLen=min(arrLen,(steps>>1)+1);
int dp[2][arrLen];
for(int i=arrLen-1;i>0;i--)
{
dp[0][i]=0;
}
dp[0][0]=1;
for(int i=1;i<=steps;i++)
{
dp[i&1][0]=(dp[~i&1][0]+dp[~i&1][1])%M;
dp[i&1][arrLen-1]=(dp[~i&1][arrLen-1]+dp[~i&1][arrLen-2])%M;
for(int j=arrLen-2;j>0;j--)
{
dp[i&1][j]=((dp[~i&1][j]+dp[~i&1][j-1])%M+dp[~i&1][j+1])%M;
}
}
return dp[steps&1][0];
}
};
四、提交记录
时间超过100%,空间超过97%的提交。
求个关注
走过路过给个关注吧,谢谢qwq。