關於矩形覆蓋面試題
之前已經在上一篇分治法面試題(一):矩形覆蓋一文中給出了該問題的遞歸解法。但是上面的分析可以看出效率不高,主要是存在大量重復元素的計算。那么如何避免大量重復元素的計算呢?這里將給出幾種解決方案。
關於動態規划
動態規划的思想與我們上篇探討的分治法相似,也是通過組合子問題的解從而得到整個問題的解。從上節給出的題目看得出來,分治分解出的子問題都是相對獨立的,但是動態規划分解的子問題通常不是獨立存在的。分治法有時候存在分解后的問題太多,因而重復計算也多的問題。那么如果能夠保存已求得的子問題的答案,從而在再次使用的時候調出,就會節省大量的時間。我們可以用一個表(數組)來存放求得的子問題的解,這就是動態規划的思想。
下面就給出集中解決方案,題目仍采用上篇所述的矩形覆蓋。
解決方案1
class Solution { public: int a[1000];//采用動態數組更好,此處僅作演示 int rectCover(int number) { if(a[number]!=0) return a[number]; if(number<=0) return 0; if(number==1) a[number]=1; else if(number==2) a[number]=2; else { int p=rectCover(number-1); int q=rectCover(number-2); a[number]=p+q; } return a[number]; } };
該解法稱為動態規划的“備忘錄”法,即帶備忘的自頂向下法。感興趣的可以拿上篇分析的rectCover(5)來試試,rectCover(1),rectCover(2)都只需要計算一次。即如果該元素已經被計算過了,那就直接“取”。
解決方案2
1 class Solution { 2 //自底向上 3 public: 4 int a[1000]; 5 int rectCover(int number) { 6 a[1]=1; 7 a[2]=2; 8 for(int i=3;i<=number;i++) 9 a[i]=a[i-1]+a[i-2]; 10 return a[number];; 11 } 12 };
事實上,解法一是動態規划的變形形式,動態規划一般的都是自底向上求解,如解法2。
解決方案3
1 class Solution { 2 public: 3 int rectCover(int number) { 4 if(number<=0) return 0; 5 if(number==1) return 1; 6 if(number==2) return 2; 7 int num1=1; 8 int num2=2; 9 int result=0; 10 number=number-2; 11 while(number--){ 12 result=num1+num2; 13 num1=num2; 14 num2=result; 15 } 16 return result; 17 } 18 };
解決方案3的最大的優勢是不需要額外的數組來存放元素。