解题思路一:(动态规划)
\qquad
假设F(n)
返回的是爬n
阶的所有方法个数,由题可知,每次可以爬1-2级台阶,那么可以得到:
\qquad
\qquad
\qquad
\qquad
\qquad
F(n) = F(n - 1) + F(n - 2)
\qquad
我们可以通过列表的方法记录每个F(n)
的值,后面的数只要不断查表并将其相加就可以了。
\qquad
虽然这个方法的时间复杂度已经是O(N)
,但空间复杂度可以进一步优化。观察计算公式,其实并不需要记录每个F(n)
的值,只记录F(n - 1)
和F(n - 2)
的值就足够了,这样空间复杂度达到了O(1)
。
int climbStairs(int n) {
int a = 0, b = 0, c = 1;
for(int i = 1; i <= n; i++)
{
a = b;
b = c;
c = a + b;
}
return c;
}
解题思路二:(快速幂)
\qquad
需要将公式F(n) = F(n - 1) + F(n - 2)
转换为矩阵计算的形式,即:
递推回去可以得到:
令:
M
=
[
1
1
1
0
]
M = \begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}
M=[1110]只要求出
M
n
M^n
Mn,就能得到F(n)
的值。快速幂快就快在对
M
n
M^n
Mn的计算上,把n
看做一个二进制数表示(如1101),每一位都有一个“基数” =
M
2
i
M^{2^i}
M2i,只要将二进制表示中为1
的数位对应的“基数”相乘,就可以得到
M
n
M^n
Mn。而每一位的“基数”可以前一位的“基数”不断平方得到,即:
M
2
i
−
1
∗
M
2
i
−
1
=
M
2
∗
2
i
−
1
=
M
2
i
M^{2^{i-1}} * M^{2^{i-1}} = M^{2*2^{i-1}} = M^{2^i}
M2i−1∗M2i−1=M2∗2i−1=M2i,这样将时间复杂度可以达到O(log N)
。
\qquad 其中,矩阵计算可以套用模版(m为左矩阵的列数): M [ i ] [ j ] = ∑ k = 0 m − 1 a [ i ] [ k ] ∗ b [ k ] [ j ] M[i][j] = \sum_{k=0}^{m-1} a[i][k]*b[k][j] M[i][j]=k=0∑m−1a[i][k]∗b[k][j]
typedef long long ll;
vector<vector<ll>> matrixMlt(vector<vector<ll>> & a, vector<vector<ll>> &b)
{
vector<vector<ll>> c(2, vector<ll>(2));
for(int i = 0; i < 2; i++)
{
for(int j = 0; j < 2; j++)
{
c[i][j] = a[i][0]*b[0][j] + a[i][1]*b[1][j];
}
}
return c;
}
int climbStairs(int n)
{
vector<vector<ll>> ans = {{1, 0}, {0, 1}};
vector<vector<ll>> std = {{1, 1}, {1, 0}};
while(n > 0)
{
if(n & 1 == 1)
{
ans = matrixMlt(ans, std);
}
std = matrixMlt(std, std);
n >>= 1;
}
return ans[0][0];
}
一点拓展:
\qquad
快速幂可以用在一些齐次线性递推式中,形如
f
(
n
)
=
∑
i
=
1
m
a
i
f
(
n
−
i
)
f(n) = \sum_{i=1}^m a_if(n-i)
f(n)=∑i=1maif(n−i),都可以列出其
M
M
M:
[
a
1
a
2
a
3
⋯
a
m
1
0
0
⋯
0
0
1
0
⋯
0
⋮
⋮
⋮
⋱
⋮
0
0
0
⋯
1
]
\begin{bmatrix} a_1 & a_2 & a_3 & \cdots & a_m \\ 1 & 0 & 0 & \cdots & 0 \\ 0 & 1 & 0 & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots &\vdots \\ 0 & 0& 0 & \cdots & 1 \end{bmatrix}
a110⋮0a201⋮0a300⋮0⋯⋯⋯⋱⋯am00⋮1
\qquad
如果不满足齐次线性递推式,可以构建
g
(
x
)
g(x)
g(x),通过添加(x-1)、(x-2) ... (x-m)
凑出如上形式,但其实不太常用,了解就好了。