1. 程式人生 > >【劍指Offer】矩形覆蓋

【劍指Offer】矩形覆蓋

題目描述

我們可以用2*1的小矩形橫著或者豎著去覆蓋更大的矩形。請問用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,總共有多少種方法?

解法1

一開始嘗試解這道題的時候其實有些不知道怎麼下手,花了很長時間。後來才發現可以利用遞迴的思想,將n的值不斷放小到某個可以直接知道結果的值。雖然直接實現遞迴的演算法可能效率不高,但在找到題目的遞迴解法後,再在遞迴演算法的基礎上做優化,就可以得到一個滿意的答案。
回到本題,用n個2*1的小矩形無重疊地覆蓋一個2*n的大矩形,假設有F(n)中方法
先用一個2*1的小矩形,豎著覆蓋大矩形,如下圖所示。則還剩下2*(n-1)的大矩形需要覆蓋,即有F(n-1)種方法

如果先用一個2*1的小矩形,橫著覆蓋大矩形,如下圖所示。則底部的紅色區域也只能用一個2*1的小矩形橫著覆蓋。則還剩下2*(n-2)的大矩形需要覆蓋,即有F(n-2)中方法

由以上兩種情況可知,F(n) = F(n - 1) + F(n - 2),我們只需要知道F(0),F(1),就可以求得F(n)。很明顯這是一個斐波那契數列的定義。對於斐波那契數列的多種求解方法可以參考【劍指Offer】斐波那契數列
當n = 0的時候,顯然有0中覆蓋方法,即F(0) = 0
當n = 1的時候,只有一種覆蓋方法,即F(1) = 1
我們可以直接使用直觀的遞迴演算法求解,如下所示

實現程式碼

public int rectCover(int number)
{
    if (number <= 0)
        return 0;
    if (number == 1)
        return 1;
    else if (number == 2)
        return 2;
    return rectCover(number - 1) + rectCover(number - 2);
}

解法2

可以使用迴圈迭代的方式優化遞迴演算法,如下所示

實現程式碼

public int rectCoverOptimize(int number)
{
    int f = 0, g = 1;
    while (number-- > 0)
    {
        g = f + g;
        f = g - f;
    }
    return f == 0 ? 0 : g;
}

解法3

既然已經知道本題實際上就是求解斐波那契數列,那麼可以利用矩陣的快速冪求解

實現程式碼

// 矩陣乘法
public int[,] matrixMul(int[,] m1, int[,] m2)
{
    int[,] ret = {
        {m1[0, 0] * m2[0,0] + m1[0, 1] * m2[1,0], m1[0, 0] * m2[0,1] + m1[0, 1] * m2[1,1]}, 
        {m1[1, 0] * m2[0,0] + m1[1, 1] * m2[1,0], m1[1, 0] * m2[0,1] + m1[1, 1] * m2[1,1]}
    };
    return ret;
}

// 矩陣快速冪
public int[,] matrixPow(int[,] m, int n)
{
    int[,] ret = { { 1, 0 }, { 0, 1 } };
    while (n > 0)
    {
        if ((n & 1)> 0){
            ret = matrixMul(ret, m);
        }
        n >>= 1;
        m = matrixMul(m, m);
    }
    return ret;
}

public int rectCoverOptimize2(int number)
{
    if (number == 0)
        return 0;
    int[,] unit = { { 1, 1 }, { 1, 0 } };
    int[,] ret = matrixPow(unit, number);
    int[,] m = { { 1, 0 }, { 0, 0 } };
    ret = matrixMul(ret, m);
    return ret[0,0];
}