一、题目描述

JZ10矩形覆盖
题目大意:
我们可以用2 * 1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2 * 1的小矩形无重叠地覆盖一个2 * n的大矩形,从同一个方向看总共有多少种不同的方法?
注意审题: 无重叠,重同一个方向看

二、算法1(递归)

算法思路

1.总体思路:根据题意,我们可以假设从左往右看,由于题目给定的小矩形的数量,以及大矩形的形状,因此我们可以发现每次,我们只能有两次选择,要么竖着放一个,要么横着放两个
2.递归边界就是当小矩形用完时结束

代码实现(C++11)

class Solution {
public:
    int rectCover(int number) {
        if(number == 0) return 0;
        return dfs(number);
    }

    int dfs(int n){
        if(n == 0) return 1;    // 刚好用完,合法
        if(n < 0) return 0;    // 非法方案
        return dfs(n - 1) + dfs(n - 2);    // 尝试两种放法
    }
};

时间复杂度:O(2^n)
空间复杂度:O(2^n)

三、算法2(动态规划)

算法思路

1.总体思路:定义状态数组f,f[i]表示当小矩形数量为i时有多少种方法,根据题意,每次可以选择竖着放一个或者横着放两个,故易知状态转移方程:
图片说明

代码实现(C++11)

class Solution {
public:
    int rectCover(int number) {
        int f[number + 1];
        f[0] = 0, f[1] = 1, f[2] = 2;
        for(int i = 3; i <= number; ++i)
            f[i] = f[i - 1] + f[i - 2];
        return f[number];
    }
};

时间复杂度:O(n)
空间复杂度:O(n)

空间复杂度优化

观察递推方程我们可以发现,每次f[i]的值只和f[i - 1]和f[i - 2]有关,因此我们可以用两个变量来表示,递推即可

代码实现(C++11)

class Solution {
public:
    int rectCover(int number) {
        if(number == 0) return 0;
        if(number == 1) return 1;
        int pre = 1, cur = 2;
        for(int i = 3; i <= number; ++i){
            int tmp = cur + pre;
            pre = cur;
            cur = tmp;
            }
        return cur;
    }
};

时间复杂度:O(n)
空间复杂度:O(1)

四、算法三(矩阵快速幂)

观察状态转移方程,我们可以发现它和斐波那契数列的定义是相同的,因此可以采用矩阵快速幂的方法来解决,详细解法参见斐波那契数列题解
时间复杂度:O(logn)
空间复杂度:O(1)