我们可以用 2*1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2*1 的小矩形无重叠地覆盖一个 2*n 的大矩形,从同一个方向看总共有多少种不同的方法?
数据范围:
进阶:空间复杂度
,时间复杂度 %5C)
进阶:空间复杂度
注意:约定 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)
代码:
public class Solution { public int rectCover(int target) { if(target <= 1){ return 1; } if(target*2 == 2){ return 1; }else if(target*2 == 4){ return 2; }else{ return rectCover((target-1))+rectCover(target-2); } } }