矩阵快速幂
问题描述: 爬楼梯需要n阶可爬到楼顶,每次只能爬1或2阶,问共有多少种爬法。
动态规划: 由于每次只能爬1或2阶,故阶数为n的总方案数为最后一步爬1阶(即阶数为n-1阶的总方案数)加上最后一步爬2阶(即阶数为n-2阶的总方案数)。
可得,状态转移方程: f(n) = f(n - 1) + f(n -2); <斐波那契数列>
边界条件: f(0) = 1,f(1) = 1;
对此状态转移方程,可使用递归或迭代法:
递归:
class Solution{ //超出时间限制 !!!!
public int climbStairs(int n){
if(n == 0) return 1;
if(n == 1) return 1;
return climbStairs(n - 1) + climbStairs(n - 2);
}
}
迭代:
class Solution{ //执行用时:0ms,击败100%
//内存消耗:35.1MB,击败79.34%
public int climbStairs(int n){
int f0 = 1,f1 = 1,fx = 0;
for(int i = 2;i <= n;i++){
fx = f0 + f1;
f0 = f1;
f1 =fx;
}
if(n == 0 || n == 1) return 1;
return fx;
}
} //时间复杂度o(n),空间复杂度o(1)
矩阵快速幂:
不是所有的非齐次线性递推都可以转换为齐次线性递推!!!
矩阵快速幂原理: 通常计算矩阵A的n次幂高达o(n^3),运算效率极低,故应将n转换为二进制数
eg: 15 = (1111) = 1 * 2^3 + 1 * 2^2 + 1 * 2^1 + 1* 2^0;
关键代码:
public int[][] pow(int[][] a, int n) {
int[][] ret = {{1, 0}, {0, 1}}; //定义单位阵
while (n > 0) {
if ((n & 1) == 1) { //若n的二进制形式该位为1,则加入结果
ret = multiply(ret, a);
}
n >>= 1;
a = multiply(a, a);
}
return ret;
}
完整代码:
class Solution {
public int climbStairs(int n) {
int[][] p = {{1, 1}, {1, 0}};//齐次线性递推构造矩阵
int[][] res = pow(p, n);
return res[0][0];
}
public int[][] pow(int[][] a, int n) {
int[][] ret = {{1, 0}, {0, 1}};
while (n > 0) {
if ((n & 1) == 1) {
ret = multiply(ret, a);
}
n >>= 1;
a = multiply(a, a);
}
return ret;
}
public int[][] multiply(int[][] a, int[][] b) { //2阶矩阵相乘
int[][] c = new int[2][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;
}
} //时间复杂度:o(logn) 空间复杂度:o(1)
斐波那契数列通项公式:
class Solution{
public int climbStairs(int n){
double sqrt5 = Math.sqrt(5);
double fibn = Math.pow((1+sqrt5)/2,n + 1) - Math.pow((1-sqrt5)/2,n + 1);
return (int)Math.round(fibn/sqrt5);
}
}