我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,从同一个方向看总共有多少种不同的方法?
数据范围:
进阶:空间复杂度 ,时间复杂度
进阶:空间复杂度 ,时间复杂度
注意:约定 n == 0 时,输出 0
比如n=3时,2*3的矩形块有3种不同的覆盖方法(从同一个方向看):
2*1的小矩形的总个数n
覆盖一个2*n的大矩形总共有多少种不同的方法(从同一个方向看)
0
0
1
1
4
5
小矩形无非两种摆法,横着摆或竖着摆。
定义 F(n),含义为在 2 * n 的区域里摆满小矩形,有几种方法?
① 第一种选择:小矩形竖着摆,就变成了 2 * (n - 1) 列的区域 --> F(n - 1)。
② 第二种选择:小矩形横着摆,下面的位置的小矩形也必须横着摆,变成了 2 * (n - 2) 列的区域 --> F(n - 2)。
import java.util.*; public class Solution { public int rectCover(int target) { if(target < 1) { return 0; } if(target == 1 || target == 2) { return target; } int[][] base = {{1, 1}, {1, 0}}; int[][] res = matrixPower(base, target - 2); return 2 * res[0][0] + res[1][0]; } public int[][] matrixPower(int[][] m, int p) { int[][] res = new int[m.length][m[0].length]; for(int i = 0; i < res.length; i++) { res[i][i] = 1; } int[][] t = m; for(; p != 0; p >>= 1) { if((p & 1) != 0) { res = multiMatrix(res, t); } t = multiMatrix(t, t); } return res; } public int[][] multiMatrix(int[][] m1, int[][] m2) { int[][] res = new int[m1.length][m2[0].length]; for(int i = 0; i < m1.length; i++) { for(int j = 0; j < m2[0].length; j++) { for(int k = 0; k < m2.length; k++) { res[i][j] += m1[i][k] * m2[k][j]; } } } return res; } }
//变相的跳台阶问题 重点在于理解这个矩形拼凑的关系 找到转移方程 //2*n大小矩阵最后结尾的那个矩形要么是一个竖的 要么是两个横的 //2*n大小的矩阵 要么是2*(n-1)大小的矩阵竖着拼一个2*1的 要么是2*(n-2)大小的矩阵横着拼两个1*2的 public class Solution { public int rectCover(int target) { if(target==0){ return 0; } if(target<=2){ return target; } int[] dp = new int[target]; dp[0] = 1; dp[1] = 2; for (int i = 2; i < dp.length; i++) { dp[i] = dp[i-1] + dp[i-2]; } return dp[dp.length-1]; } }
/** * 矩形覆盖 * 状态转移方程: dp[i] = dp[i-1] + dp[i-2]; * 本质上和上楼梯是一样的 要么一次一格要么一次两格 * @param target * @return */ public int rectCover(int target) { int[] dp = new int[target + 1]; //base case if(target >= 1) dp[1] = 1; if(target >=2) dp[2] = 2; for(int i = 3; i<= target; i++){ dp[i] = dp[i-1] + dp[i-2]; } return dp[target]; }
public class Solution { public int rectCover(int target) { if(target == 0){ return 0; } else if(target == 1){ return 1; } else if(target == 2){ return 2; } int a = 1; int b = 2; for(int i = 3; i <= target; i++){ int temp = a + b; a = b; b = temp; } return b; } }
public class Solution { public int rectCover(int target) { if(target<=2){ return target; } return rectCover(target-1)+rectCover(target-2); } }
public class Solution { public int rectCover(int target) { // 递推,可转化为斐波那契数列 // f(n)=f(n-1)+f(n-2) int a=0; int b=1; int sum = 0; for(int i=0;i<target;i++){ sum = a+b; a = b; b = sum; } return sum; } }
public class Solution { private int rst=0; public int rectCover(int target) { if(target==0) return 0; dfs(target,1); dfs(target,2); return rst; } public void dfs(int target,int width){ if(target==width) rst++; else if(target<width) return; else{ dfs(target-width,1); dfs(target-width,2); } } }
public class Solution { public int rectCover(int target) { int a= 2; int b=1; if(target==0||target==1 || target==2) return target; for(int i = 3; i<=target;i++){ a=a+b; b=a-b; } return a; } }时间复杂度O(n), 空间复杂度O(1)
public class Solution { public int rectCover(int target) { if (target == 0) { return 0; } return getResult(target); } private int getResult(int target) { if (target == 0) { return 1; } if (target == 1) { return 1; } return getResult(target - 1) + getResult(target - 2); } }动态规划法:
public class Solution { public int rectCover(int target) { if (target == 0 || target == 1) { return target; } return getResult(target); } // 动态规划解法: private int getResult(int target) { int[] dp = new int[target + 1]; dp[0] = 1; //这里是为了方便才设为1 dp[1] = 1; for (int i = 2; i <= target; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[target]; } }
public int rectCover(int target) { if(target < 0) return -999; if(target == 0) return 0; int a = 1,b = 2; for(int i = 1;i < target;i++){ int temp = a + b; a = b; b = temp; } return a; }
/** 本题思路:分析小方块组合可能 矩形所有情况都包括在这两种可能里:以一块竖块或是两块横块结尾 观察可发现:2*(n-2)的情况下在结尾添加两个横块 或2*(n-1)的情况下结尾添加一个竖块即可满足2*n的所有情况 所有则有 f(n) = f(n-1) + f(n-2) 即斐波那契数列 */ public class Solution { public int rectCover(int target) { int[] res = new int[]{0,1,2}; if (target < 3) { return res[target]; } for (int i = 3; i<=target; ++i) { res[0] = res[2]; res[2] = res[1] + res[2]; res[1] = res[0]; } return res[2]; } }
/** * 解题思路: * * 动态规划。x(target) = x(target-1) + x(target-2) * * @author freedom wang * @date 2021-01-23 10:48:08 */ public int rectCover(int target) { if (target == 0 || target == 1) { return target; } int[] array = new int[target + 1]; array[0]= 1; array[1]= 1; for (int i = 2; i < target + 1; i++) { array[i] = array[i-1] + array[i-2]; } return array[target]; }
第一次摆放一块1*2的小矩阵,则摆放方法总共为f(target-2)
代码: