文章目录
类似斐波那契数列的递归+快速幂
如果某个递归,除了初始项之外,具有如下的形式
F(N) = C1 * F(N) + C2 * F(N-1) + … + Ck * F(N-k) ( C1…Ck 和k都是常数)
并且这个递归的表达式是严格的、不随条件转移的
那么都存在类似斐波那契数列的优化,时间复杂度都能优化成O(logN)
马走棋盘问题
马从(0,0)出发,到达指定的(x,y)位置,必须走k步数,一共有多少种方法?
(马只能走日)
对于每个点,到达他的位置有8个位置(不越界的前提下)
public static int f(int х,int y,int k){
if(k ==0) {
returnх==0&&y==0?1:0;
}
if(x_ < 0 || х> 9 || у<0 || у>8) {
return 0;
}
//有步数要走,x,y也是棋盘上的位置
return
f (x+2,y-1, k-1)
+f(x + 2, y+1, k-1)
+ f(х+1,y+2,k-1)
+f(x-1,у+2,k-1)
+ f(x- 2,y+1,k-1)
+f(x - 2, y-1, k-1)
+ f(x-1,y-2,k-1)
+f(x + 1, у-2, k-1);
}
改动态规划
因为是后一步依赖前一步,所以先准备第一步的数据,逐步往前,直到最后一步。
public static int ways2(int x, int y, int k) {
int[][][] dp = new int[10][9][k+1];// O~k
dp[0][0][0] = 1; // dp[..][..][0] = 0
for(int level = 1; level <= k; level++) {
for(int x =0;x<10;x++){
//x可能性
for(int y =0; y < 9;y++){
// 求 dp[i][j][level]; 递归函数怎么求的。这里也怎么求
dp[i][j][level]=
getValue(dp,x+2,y-1, level-1)
+getValue(dp,x + 2, y+1, level-1)
+getValue(dp,х+1,y+2,level-1)
+getValue(dp,x-1,у+2,level-1)
+getValue(dp,x- 2,y+1,level-1)
+getValue(dp,x - 2, y-1, level-1)
+getValue(dp,x-1,y-2,level-1)
+getValue(dp,x + 1, у-2, level-1);
;
}
}
}
return dp[x][y][k];
}
public static int getValue(int[][][] dp, int x, int y, int k) {
if(x<0 || x>9 || y<0 || y>8){
return 0;
}
return dp[x][y][k];
}
斐波那契数列快速幂解
暴力解复杂度为
O(2^N)
快速求 10^75.
75转为2进制 1001011
int res=0;
intemp=10
t每次和自己相乘,10^1 10^2. 10^4. 10^8. 10^16……
然后判断对应二进制位是否为1 ,如果为1 ,res= res * t
对于矩阵,每次也是 M^1 M^2 ……
对应二进制位为1 乘进res中
剑指 Offer 10- I. 斐波那契数列
class Solution {
public int fib(int n) {
if(n==0)return 0;
if(n==1||n==2)return 1;
int[][]base={
{
1,1},
{
1,0}
};
int [][] res = matrixPower(base,n-1);
return res[0][0];
}
public int[][]matrixPower(int [][] m, int temp){
int[][] ret = {
{
1, 0}, {
0, 1}};
while(temp!=0){
// 如果对应二进制位为1 则乘进去
if((temp&1)!=0){
ret = muliMatrix(ret, m);
}
m = muliMatrix(m, m);
temp>>=1;
}
return ret;
}
public static int[][] muliMatrix(int[][] a, int[][] b) {
int[][] c = new int[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = (int) (((long) a[i][0] * b[0][j] + (long) a[i][1] * b[1][j]) % 1000000007);
}
}
return c;
}
}
值得记忆
//当二进制不为0时
while(temp!=0){
// 如果对应二进制位为1 则乘进去
if((temp&1)!=0){
ret = muliMatrix(ret, m);
}
//如果二进制位为0,则自乘
m = muliMatrix(m, m);
// 右移
temp>>=1;
}
上楼问题
一个人可以一次往上迈1个台阶,也可以迈2个台阶
返回这个人迈上N级台阶的方法数
第n阶可以从n-1阶台阶到达,也可以从n-2阶台阶到达:
F(n)=F(n-1)+F(n-2)
若一个人一次可以上k步,p步,m步
求到N级台阶
F(n)=f(n-k)+f(n-p)+f(n-m)
矩阵为 max(k,p,m) * max(k,p,m)
母牛问题
第一年农场有1只成熟的母牛A,往后的每年:
1)每一只成熟的母牛都会生一只母牛
2)每一只新出生的母牛都在出生的第三年成熟
3)每一只母牛永远不会死
返回N年后牛的数量
n年的牛由n-1年牛的个数+n-3年牛个数(满三年都生一只小牛)
F(n)=F(n-1)+F(n-3)
字符问题
给定一个数N,想象只由0和1两种字符,组成的所有长度为N的字符串
如果某个字符串,任何0字符的左边都有1紧挨着,认为这个字符串达标
返回有多少达标的字符串
n位数,最左边一位为1有:F(n-1)种方法;最左边为0则倒数第二位必须为1,则有F(n-2)种方法
F(n)=F(n-1)+F(n-2)
瓷砖问题
用12的瓷砖,把N2的区域填满
返回铺瓷砖的方法数
第一个瓷砖竖着排,则后面有F(n-1)种方法;第一个瓷砖横着排,则后面有F(n-2)种方法
F(n)=F(n-1)+F(n-2)