八皇后之回溯法解決[轉]

來源:互聯網
上載者:User

標籤:

問題描述:

要在8*8的國際象棋棋盤中放8個皇后,使任意兩個皇后都不能互相吃掉。規則是皇后能吃掉同一行、同一列、同一對角線的棋子。如即是兩種方案:

 

解決方案:

8*8的棋盤要擺放8個皇后,且不能同行同列同對角線,那麼每行必定會有一個皇后。我們可以設一個數組a用來存放每一行皇后的位置,元素值表示第幾列(如a[1]=5表示第一行的皇后處於第五個格)。然後只需要求出數組a的值 問題就解決了,下面介紹三種回溯解法:

 

1、八個for迴圈。用枚舉的辦法,八個for迴圈分別枚舉每一行的8個位置,但是我們不用全部枚舉完,可以採用“剪枝策略”,即遇到不適合的情況就回溯。例如當a[1]=4,第二行a[2]=4與a[1]同列,不符合題意。接下來的六個迴圈就不用窮舉下去了,直接"continue;"去檢驗a[2]=5.....具體代碼如下:

 

[cpp] view plaincopyprint? 
  1. void main()  
  2. {  
  3.     int a[9];  
  4.     int i,t=1;  
  5.     for(a[1]=1;a[1]<9;a[1]++)  
  6.         for(a[2]=1;a[2]<9;a[2]++)  
  7.         {  
  8.             if(!Check(a,2)) continue;  
  9.             for(a[3]=1;a[3]<9;a[3]++)  
  10.             {  
  11.                 if(!Check(a,3)) continue;  
  12.                 for(a[4]=1;a[4]<9;a[4]++)  
  13.                 {  
  14.                     if(!Check(a,4)) continue;  
  15.                     for(a[5]=1;a[5]<9;a[5]++)  
  16.                     {  
  17.                         if(!Check(a,5)) continue;  
  18.                         for(a[6]=1;a[6]<9;a[6]++)  
  19.                         {  
  20.                             if(!Check(a,6)) continue;  
  21.                             for(a[7]=1;a[7]<9;a[7]++)  
  22.                             {  
  23.                                 if(!Check(a,7)) continue;  
  24.                                 for(a[8]=1;a[8]<9;a[8]++)  
  25.                                 {  
  26.                                     if(!Check(a,8)) continue;  
  27.                                     else   
  28.                                     {  
  29.                                         printf("第%d種解法:\n",t++);  
  30.                                         for(i=1;i<9;i++)  
  31.                                             printf("第%d個皇后:%d\n",i,a[i]);  
  32.                                         printf("\n\n");  
  33. }       }   }   }   }   }   }   }   }  

 

[cpp] view plaincopyprint? 
  1. /////////////////////////////////Check函數功能:檢驗第n行的皇后是否和之前的皇后有衝突,沒有的話返回1  
  2. int Check(int a[],int n)  
  3. {  
  4.     for(int i=1;i<n;i++)  
  5.     {  
  6.         if(abs(a[i]-a[n])==abs(i-n) || a[i]==a[n])//////////////見下面注釋  
  7.             return 0;  
  8.     }  
  9.     return 1;  
  10. }  

 

代碼注釋:

 

某一行的皇后a[n]不能和之前的皇后a[i]位置有衝突,約束條件為:

a、不在同一列:a[n] != a[i]

b、不在同一行:因為現在是每一行求一個皇后的位置,所以同一行不會有衝突,不需要考慮。

c、不在同一對左角線:a[n]-a[i] != n-i

d、不在同一右對角線:a[n]-a[i] != -(n-i)

條件c和d可以合成:abs(a[n]-a[i]) != abs(n-i)

總結:其實這裡用到的就是深度優先搜尋的思想,從第一行的皇后一直深入去找第二行、第三行...皇后的位置。其中加上了約束條件Check函數進行“剪枝”。這就是回溯演算法的思想:深度優先搜尋,遇到不滿足的情況就進行回溯。

 

 

2、方法一的最佳化。上述代碼易讀、易懂,但是用八個for迴圈不免顯得很累贅,而且如果要求在100*100的棋盤上放100個皇后這種“N皇后問題“呢?難道用100個for迴圈嗎?我們來把代碼最佳化一下,用到的思想還是和方法一相同:深度優先搜尋、回溯。具體代碼如下:

 

[cpp] view plaincopyprint? 
  1. void main()  
  2. {  
  3.     int a[256]={0};  
  4.     int i=1,j,n,t=1;////////////////////////////////////i表示當前正在搜尋第i行的皇后位置  
  5.     printf("請輸入幾皇后?n=");  
  6.     scanf("%d",&n);  
  7.     while(i>0)  
  8.     {  
  9.         for(a[i]++;a[i]<=n;a[i]++)  
  10.         {  
  11.             if(Check(a,i))//////////////////////////////如果第i行的皇后與之前的皇后位置上沒有衝突,則break跳出迴圈  
  12.                 break;  
  13.         }  
  14.         if(a[i]<=n)/////////////////////////////////////如果a[i]<=n,即上面的for迴圈是由“break;”跳出來的,即第i行皇后的位置符合條件  
  15.         {  
  16.             if(i==n)////////////////////////////////////找到一組解,輸出  
  17.             {  
  18.                 printf("第%d種解法:\n",t++);  
  19.                 for(j=1;j<=n;j++)  
  20.                     printf("第%d個皇后:%d\n",j,a[j]);  
  21.                 printf("\n\n");  
  22.             }  
  23.             else////////////////////////////////////////未找完  
  24.             {  
  25.                 i++;  
  26.                 a[i]=0;  
  27.             }  
  28.         }  
  29.         else  
  30.             i--;////////////////////////////////////////回溯  
  31.     }  
  32. }  

代碼注釋:上面用到的Check函數和方法一的Check函數相同。

 

總結:雖然上面代碼中只用到兩層迴圈,但是思想、思路和方法一都是一樣的,時間複雜度也是和方法一的時間複雜度相同。當n大於10之後運算就已經比較困難了。

 

 

3、遞迴實現。上面兩種方法都是用到了深度優先搜尋,而一般而言,深度優先搜尋都是可以用遞迴來實現的。下面我們用遞迴的方式解決八皇后問題。具體代碼如下:

 

[cpp] view plaincopyprint? 
  1. int a[20],n,i,j,t=1;////////////////////////////////////////全域變數  
  2.   
  3. void Try(int i)  
  4. {  
  5.     int j,k;  
  6.     for(j=1;j<=n;j++)  
  7.     {  
  8.         a[i]=j;  
  9.         if(Check(a,i))///////////////////////////////////////如果第j列不會與之前的皇后衝突  
  10.         {  
  11.             if(i<n)//////////////////////////////////////////如果i<n,即還沒有找到八個皇后,繼續遞迴  
  12.                 Try(i+1);  
  13.             else ////////////////////////////////////////////如果找到了一組解就輸出  
  14.             {  
  15.                 printf("第%d種解法:\n",t++);  
  16.                 for(k=1;k<=n;k++)  
  17.                     printf("第%d個皇后:%d\n",j,a[k]);  
  18.                 printf("\n\n");  
  19. }   }   }   }  
  20.   
  21. void main()  
  22. {  
  23.     printf("幾皇后?n=");  
  24.     scanf("%d",&n);  
  25.     Try(1);  
  26. }  

 

 代碼注釋:

a、此處遞迴的思路很簡單,每一層遞迴表示一行皇后,j表示列,即a[i]=j表示第i行的皇后位置在第j列。

b、以上用到的Check函數與方法一的Check函數相同。

我不清楚是什麼原因,遞迴的速度竟然明顯比前面的兩種方法快??

 

轉自:http://blog.csdn.net/bone_ace/article/details/41419695

八皇后之回溯法解決[轉]

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.