Leetcode62. Unique Paths-动态规划/数学公式
题目
思路
两种思路:1.动态规划 2.数学公式
- 动态规划1
用一个表 d p dp dp记录每个位置的总数,每个位置上只能从左或从上到达,因此值是上边格子的值加上左边格子的值:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][j] = dp[i - 1][j] + dp[i][j - 1] dp[i][j]=dp[i−1][j]+dp[i][j−1]
1 | 1 | 1 | 1 | 1 |
---|---|---|---|---|
1 | 2 | 3 | 4 | 5 |
1 | 3 | 6 | 10 | 15 |
1 | 4 | 10 | 20 | 35 |
- 动态规划2
优化空间复杂度
由于第一行和第一列一定都是1,每次只是更新红框内的行,因此只记录这一行即可
- 数学公式
由于从左上角到右下角,一定有m - 1次向下,n - 1次向右,
走过路径长度一定为(m - 1) + (n - 1),
因此总路径数就是在这(m - 1) + (n - 1)中选出m - 1个向下,或n - 1个向右的组合数,
即 C m + n − 2 m − 1 C_{m+n-2}^{m-1} Cm+n−2m−1,或 C m + n − 2 n − 1 C_{m+n-2}^{n-1} Cm+n−2n−1
对组合数 C a + b a = ( a + b ) ! a ! b ! C_{a+b}^{a}=\frac{(a+b)!}{a!b!} Ca+ba=a!b!(a+b)!,
如果直接求三个阶乘数,可能时间更长,考虑到分子和分母都从1开始累乘,可以进行化简:
C a + b a = ( a + b ) ! a ! b ! = ( ∏ i = m a x ( a , b ) a + b i ∗ ∏ i = 1 m a x ( a , b ) i ) / ( ∏ i = 1 m i n ( a , b ) i ∗ ∏ i = 1 m a x ( a , b ) i ) = ∏ i = m a x ( a , b ) a + b i / ∏ i = 1 m i n ( a , b ) i C_{a+b}^{a}=\frac{(a+b)!}{a!b!}=(\prod \limits_{i=max(a,b)}^{a+b} i* \prod \limits_{i=1}^{max(a,b)}i)/(\prod \limits_{i=1}^{min(a,b)}i * \prod \limits_{i=1}^{max(a,b)}i)=\prod \limits_{i=max(a,b)}^{a+b}i/\prod \limits_{i=1}^{min(a,b)}i Ca+ba=a!b!(a+b)!=(i=max(a,b)∏a+bi∗i=1∏max(a,b)i)/(i=1∏min(a,b)i∗i=1∏max(a,b)i)=i=max(a,b)∏a+bi/i=1∏min(a,b)i
可见可以省去两次 ∏ i = 1 m a x ( a , b ) i \prod \limits_{i=1}^{max(a,b)}i i=1∏max(a,b)i的求积
复杂度
- 时间复杂度
- 填表,表格尺寸为 m ∗ n m*n m∗n, O ( m ∗ n ) \mathcal{O}(m*n) O(m∗n)
- DP优化空间,也要计算每个位置的值, O ( m ∗ n ) \mathcal{O}(m*n) O(m∗n)
- 数学公式,计算乘积次数为 m + n − 2 − m a x ( m − 1 , n − 1 ) + m i n ( m − 1 , n − 1 ) m+n-2 - max(m-1, n-1) + min(m-1, n - 1) m+n−2−max(m−1,n−1)+min(m−1,n−1),因此为 O ( m + n ) \mathcal{O}(m+n) O(m+n)
- 空间复杂度
- DP填表,表格尺寸为 m ∗ n m*n m∗n, O ( m ∗ n ) \mathcal{O}(m*n) O(m∗n)
- DP优化:只记录一行, O ( n ) \mathcal{O}(n) O(n)
- 数学公式,每次调用只有几个变量, O ( 1 ) \mathcal{O}(1) O(1)
代码
DP- O ( m ∗ n ) \mathcal{O}(m*n) O(m∗n)时间, O ( m ∗ n ) \mathcal{O}(m*n) O(m∗n)空间
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if m == 1 or n == 1:
return 1
# 保留整个dp列表
dp = [[0 for _ in range(n)] for _ in range(m)]
for i in range(m):
dp[i][0] = 1
for j in range(n):
dp[0][j] = 1
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[-1][-1]
DP- O ( m ∗ n ) \mathcal{O}(m*n) O(m∗n)时间, O ( n ) \mathcal{O}(n) O(n)空间
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if m == 1 or n == 1:
return 1
dp = [1 for _ in range(n - 1)]
for i in range(m - 1):
for j in range(n - 1):
# 只更新这一行
if j == 0:
dp[j] += 1
else:
dp[j] += dp[j - 1]
return dp[-1]
数学公式法- O ( m + n ) \mathcal{O}(m+n) O(m+n)时间, O ( 1 ) \mathcal{O}(1) O(1)空间
def mul_between(n1, n2):
"""n1到n2间的所有自然数的乘积"""
result = 1
for n in range(n1, n2 + 1):
result *= n
return result
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
if m == 1 or n == 1:
return 1
return mul_between(max(m, n), m - 1 + n - 1) // mul_between(1, min(m, n) - 1)