【劍指Offer】矩形覆蓋
阿新 • • 發佈:2019-06-12
題目描述
我們可以用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];
}