Number of Ways to Reach a Position After Exactly k Steps

Number of Ways to Reach a Position After Exactly k Steps

You are given two positive integers startPos and endPos . Initially, you are standing at position startPos on an infinite number line. With one step, you can move either one position to the left, or one position to the right.

Given a positive integer $k$, return the number of different ways to reach the position endPos starting from startPos , such that you perform exactly $k$ steps. Since the answer may be very large, return it modulo ${10}^{9} + 7$.

Two ways are considered different if the order of the steps made is not exactly the same.

Note that the number line includes negative integers.

Example 1:

Input: startPos = 1, endPos = 2, k = 3
Output: 3
Explanation: We can reach position 2 from 1 in exactly 3 steps in three ways:
- 1 -> 2 -> 3 -> 2.
- 1 -> 2 -> 1 -> 2.
- 1 -> 0 -> 1 -> 2.
It can be proven that no other way is possible, so we return 3.

Example 2:

Input: startPos = 2, endPos = 5, k = 10
Output: 0
Explanation: It is impossible to reach position 5 from position 2 in exactly 10 steps.

Constraints:

$1 \leq startPos, endPos, k \leq 1000$

 

解题思路

  比赛的时候是用动态规划写的,没想到还有数学的解法。

  先算一下从起点到终点要走的步数,假设是$d$,即从起点到终点要净走$d$步。假设起点在终点的左边,往右走的步数记为$r$,往左走的步数记为$l$,那么要满足

\begin{cases}
r - l = d \\
r + l = k
\end{cases}

解得$r = \frac{d + k}{2}$,相当于要从$k$步中选出$r$步向右走,即$C_{k}^{r}$。算这个组合数可以直接用公式加快速幂求逆元。

  AC代码如下,时间复杂度为$O(nlogn)$:

 1 class Solution {
 2 public:
 3     int mod = 1e9 + 7;
 4 
 5     int qmi(int a, int k, int p) {
 6         int ret = 1;
 7         while (k) {
 8             if (k & 1) ret = 1ll * ret * a % p;
 9             a = 1ll * a * a % p;
10             k >>= 1;
11         }
12         return ret;
13     }
14 
15     int numberOfWays(int startPos, int endPos, int k) {
16         int d = abs(endPos - startPos);
17         if (d + k & 1 || d > k) return 0;   // 特判掉d和k奇偶不同与无法到达终点的情况
18         int ret = 1, r = d + k >> 1;
19         for (int i = 1, j = k; i <= r; i++, j--) {
20             ret = (1ll * ret * j) % mod * qmi(i, mod - 2, mod) % mod;
21         }
22         return ret;
23     }
24 };

  下面再讲讲用dp的做法。

  定义状态$f(i,j)$表示所有从起点花$i$步走到位置$j$的方案的集合,属性就是计数。根据最后一步的不同来划分集合,因此状态转移方程为$$f(i,j) = f(i-1,j-1) + f(i-1,j+1)$$

  还需要注意下标可能会出现负数的情况,因此需要加上一个偏移量。可以发现如果起点在$1$这个位置,那么最多向左$500$步,因此左边界最远为$-500$。同理如果起点在$1000$这个位置,那么最多向右走$500$步,因此右边界最远为$1500$。因此下标的最大长度为$2000$,一开始可以给起点和终点加上偏移量$500$。

  AC代码如下,时间复杂度为$O(n^2)$:

 1 class Solution {
 2 public:
 3     int mod = 1e9 + 7, N = 2010, B = 500;
 4 
 5     int numberOfWays(int startPos, int endPos, int k) {
 6         startPos += B, endPos += B; // 加上偏移量
 7         vector<vector<int>> f(k + 1, vector<int>(N));
 8         f[0][startPos] = 1;
 9         for (int i = 1; i <= k; i++) {
10             for (int j = 0; j < N; j++) {   // 直接枚举加上偏移量后的位置
11                 if (j) f[i][j] = f[i - 1][j - 1];   // 可以从左边走过来
12                 if (j + 1 < N) f[i][j] = (f[i][j] + f[i - 1][j + 1]) % mod; // 可以从右边走过来
13             }
14         }
15         return f[k][endPos];
16     }
17 };

 

参考资料

  力扣第309场周赛:https://www.bilibili.com/video/BV1DB4y1G7gX

posted @ 2022-09-05 20:16  onlyblues  阅读(58)  评论(0编辑  收藏  举报
Web Analytics