找出沒有相鄰的1的二進位制數的個數---2013年2月17日

weixin_33763244發表於2017-11-13
   問題描述:用G(n)表示在有n位的二進位制數中沒有相鄰的兩個1的二進位制數個數。比如,當n=3時,000,001,010,011,100,101,110,111這8個數中只有000,001,010,100,101這5個是沒有相鄰為1的,故G(3)=5。請寫一個程式,輸出G(n)的值。
      錯誤的思路(考慮的不周全):採用"分治"的方法,比如n=3,每次都將處理原問題規模的二分之一,直到n=1時,就很容易可以知道有兩種情況。前面是"分",然後是"合"。比如把規模為n的問題分成了p1和p2部分,而且p1和p2問題都已經解決,個數已經知道。那麼,把p1和p2合起來的時候就只需要注意p1的最右邊和p2的最左邊的數不能同時為1,即:合起來的總的個數是:1*(p2-1)+(p1-1)*p2。"遞迴"和"分治"不分家,所以很容易寫出下面的程式。
      以下是錯誤的程式碼。
錯誤的程式碼
1 int calculate(int n)
2  {
3      if(n==1)
4          return 2;
5      if(p[n]!=0)
6          return p[n];
7      int div=n/2;
8      return (p[n]=calculate(n-div)-1 + (calculate(div)-1)*calculate(n-div));
9  }
       正確的思路:演算法的核心思想不變,依然是"分治"。"分"的部分很好處理,"合"的時候就容易出錯了。我之前就是在合併的時候沒有考慮周全。依然是把規模為n的問題分成p1和p2部分,令div=n/2,那麼p1中有div個元素,p2中有n-div個元素。合併的時候,已經保證p1,p2部分分別都沒有相鄰的兩個1了,這時需要注意的就只是p1的最右邊一個數和p2的最左邊的一個數了。分兩種情況(要用到排列組合的基礎知識):
       將p1中最右邊的數記為A,p2中最左邊的數記為B,方便下面的文字描述。
       1.A不為1。這種情況比下一種情況簡單一些。在p1中,A不為1,那麼A就是0了。這種情況下,G(div)=G(div-1)。再來考慮p2。既然A為0,那麼B即使為1也沒關係了。所以,這種情況下的結果就是G(div-1)*G(n-div)。
       2.A為1。因為A為1,那麼A左邊的第一個數必然為0(因為p1中沒有兩個相鄰的1),所以在這種情況下G(div)=G(div-2)。再來考慮p2。既然A為1,那麼B肯定得為0了,那麼B右邊的數也就沒有限制了,即G(n-div-1)。所以,這種情況下的結果就是G(div-2)*G(n-div-1)。
       程式碼如下:
 1 #include <stdio.h>
 2 #define MAX 1000
 3 
 4 int p[MAX]={0};
 5 
 6 //prototype
 7 int calculate(int n);
 8 
 9 int main()
10 {
11     int n=6;
12     int result=calculate(n);
13     printf("%d\n",result);
14     return 0;
15 }
16 
17 int calculate(int n)
18 {
19     if(n<1)
20         return 1;
21     if(n==1)
22         return 2;
23     if(p[n]!=0)
24         return p[n];
25     int div=n/2;
26     return (p[n]=(calculate(div-2) * (calculate(n-div-1)) + calculate(div-1)*calculate(n-div)));
27 }
 
       通過程式,可以得到G(1)=2,G(2)=3,G(3)=5,G(4)=8,G(5)=13,G(6)=21......可以看出這是一個斐波拉契數列。
本文轉自NeilHappy 51CTO部落格,原文連結:http://blog.51cto.com/neilhappy/1134394,如需轉載請自行聯絡原作者

相關文章