1. 程式人生 > >【藍橋杯-遞歸回溯】八皇后問題+N皇后問題

【藍橋杯-遞歸回溯】八皇后問題+N皇后問題

大致思路

其實就是三個功能函式:place attack output_solution

place函式中的任務就是把所有的(設為有maxqueen個)皇后的列位置安頓好。其傳入的引數僅一個,為皇后的序數q,然後經過i從1~maxqueen的遍歷找到該序數q的皇后應在的列數號,使queen[q]=i,條件就是attack(q,i)==false。(那麼該皇后的行數號呢?這個要理解,皇后的行數號即其序數號,即q。) 設好列數號之後判斷一下是否q==maxqueen-1也就是最後一個皇后放好了,若是,則呼叫output_solution輸出該最終棋盤佈局。

attack函式就是判斷皇后衝不衝突的。其實就是判斷在他(q)之前放好的皇后的列數和q的列數(queen[q])相不相等,然後看看是不是在同一對角線上。

output_solution就是輸出最終的棋盤佈局。

  1. /* 
  2.  * 
  3.  * 【問題描述】在一個8×8的國際象棋棋盤上放置8個皇后, 
  4.  * 要求每個皇后兩兩之間不“衝突”,即沒有一個皇后能“吃 
  5.  * 掉”任何其他一個皇后,簡單的說就是沒有任何兩個皇后 
  6.  * 佔據棋盤上的同一行或同一列或同一對角線,即在每一橫 
  7.  * 列、豎列、斜列都只有一個皇后。 
  8.  * 
  9.  * 遞迴法求出8個皇后問題的解 
  10.  * 本程式使用一維陣列表示皇后的位置,queen[i]的值表示第i行皇后所在的列 
  11.  * 
  12.  * 本程式通過修改巨集定義MAXQUEEN的值,可以解決N皇后問題。 
  13.  * 
  14.  */
  15. #include <stdio.h>
  16. #include <conio.h>
  17. #define TRUE 1
  18. #define FALSE 0
  19. #define MAXQUEEN 8
  20. #define ABS(x) ((x>0)?(x):-(x))  /*求x的絕對值*/
  21. /*存放8個皇后的列位置,陣列下標為皇后的列位置*/
  22. int queen[MAXQUEEN];  
  23. int total_solution = 0;  /*計算共有幾組解*/
  24. /*函式原型宣告*/
  25. void place(int);  
  26. int attack(int,int);  
  27. void output_solution();  
  28. int main(void)  
  29. {  
  30.     place(0); /*從第0個皇后開始擺放至棋盤*/
  31.     return 0;  
  32. }  
  33. /* 遞迴放置皇后子程式 */
  34. void place(int q)  //關鍵!
  35. {  
  36.     int i=0;  
  37.     while(i<MAXQUEEN)  
  38.     {  
  39.         if(!attack(q, i)) /* 皇后未受攻擊 */
  40.         {  
  41.             queen[q]=i; /* 儲存皇后所在的列位置 */
  42.             /* 判斷是否找到一組解 */
  43.             if(q==MAXQUEEN-1)  
  44.                 output_solution(); /* 輸出此組解 */
  45.             else
  46.                 place(q+1); /* 否則繼續擺下一個皇后 */
  47.         }  
  48.         i++;  
  49.     }  
  50. }  
  51. /* 測試在(row,col)上的皇后是否遭受攻擊若遭受攻擊則返回值為1,否則返回0 */
  52. int attack(int row, int col)  
  53. {  
  54.     int i, atk=FALSE;  
  55.     int offset_row, offset_col;  
  56.     i=0;  
  57.     while(!atk && i<row)  
  58.     {  
  59.         offset_row=ABS(i-row);  
  60.         offset_col=ABS(queen[i]-col);  
  61.         /* 判斷兩皇后是否在同一列,是否在同一對角線 */
  62.         /* 若兩皇后在同列或同對角線,則產生攻擊,atk==TRUE */
  63.         atk = (queen[i] == col) || (offset_row == offset_col);  
  64.         i++;  
  65.     }  
  66.     return atk;  
  67. }  
  68. /* 輸出8個皇后的解 */
  69. void output_solution()  
  70. {  
  71.     int x,y;  
  72.     total_solution += 1;  
  73.     printf("Solution#%3d\n\t",total_solution);  
  74.     for(x=0;x<MAXQUEEN;x++)  
  75.     {  
  76.         for(y=0;y<MAXQUEEN;y++)  
  77.         if(y==queen[x])  
  78.             printf("Q"); /* 用字母Q表示皇后 */
  79.         else
  80.             printf("-"); /* 用-表示空白 */
  81.         printf("\n\t");  
  82.     }  
  83.     printf("\n");  
  84.     getchar();  
  85. }  
自己現寫的程式碼如下:

#include<iostream>
#include<bits/stdc++.h>
using namespace std;


int q[9];
int maxx=0;
int a[9][9];


bool attack(int p,int qq)
{
    if(p==1)
        return false;
    else
    {
        for(int i=1;i<p;i++)
        {
            if(q[i]==qq)
                return true;
            if(abs(q[i]-qq)==abs(i-p))
                return true;
        }
        return false;
    }
}


void place(int p)
{
    if(p==9)
    {
        int sum=0;
       for(int i=1;i<=8;i++)
       {
           sum+=a[i][q[i]];
       }
        if(maxx<sum)
            maxx=sum;
        return;
    }
    for(int i=1;i<=8;i++) //對第p行的列上遞迴
    {
        if(attack(p,i)==false)
        {
            q[p]=i;
            place(p+1);
        }
    }
}


int main()
{
    for(int i=1;i<=8;i++)
        for(int j=1;j<=8;j++)
            cin>>a[i][j];
    memset(q,0,sizeof(q));
    place(1);
    cout<<maxx;
    return 0;
}


有必要理解的是,這輸出的是多個解。因為place函式裡的那個遞迴啊,之後又i++,也就是序數為q的同一個皇后就算在這一列可以放上,那就將就著去進入遞迴,弄出一種解決方案,但是!之後仍由著while語句遍歷去往下一行嘗試。

這就是遞迴的魅力。你在leetcode上求subset的遞迴也是這個理兒啊親——求多種可能!今天明白了原來這叫回溯法

而對於N皇后問題,其實就是把maxQUEEN換成給定的n即可。

以下是原作者海島Blog發現因為題目要輸入多個n求對應的解決方案的個數,每次都去重新算導致Time Limit Exceeded,於是打表 一次性算出1~自己取的num,到時候輸入n(題中輸入的n<num)直接取ans[n]即可。

N皇后問題

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 27039    Accepted Submission(s): 12035


Problem Description在N*N的方格棋盤放置了N個皇后,使得它們不相互攻擊(即任意2個皇后不允許處在同一排,同一列,也不允許處在與棋盤邊框成45角的斜線上。
你的任務是,對於給定的N,求出有多少種合法的放置方法。


Input共有若干行,每行一個正整數N≤10,表示棋盤和皇后的數量;如果N=0,表示結束。
Output共有若干行,每行一個正整數,表示對應輸入行的皇后的不同放置數量。
Sample Input1850
Sample Output19210
Authorcgf
Source

問題簡述:(略)。

問題分析:這是一個經典的回溯法程式,是用遞迴來實現的,其實也可以用非遞迴來實現。回溯法並不是必須用遞迴來實現的。

程式說明:程式的細節還是需要注意的,例如打表時,需要多宣告一個元素,因為陣列下標是從0開始。

原先做過解N皇后問題的程式,就拿來簡單改寫了一下。有關程式,參見:八皇后(N皇后)問題演算法程式
一提交,“Time Limit Exceeded”,只好先打表。原來的程式碼註釋留在那裡了。


AC的C語言程式如下:

  1. /* HDU2553 N皇后問題 */
  2. #include <stdio.h>
  3. #define TRUE 1
  4. #define FALSE 0
  5. #define MAXQUEEN 10
  6. #define ABS(x) ((x>0)?(x):-(x))  /*求x的絕對值*/
  7. /*存放8個皇后的列位置,陣列下標為皇后的列位置*/
  8. int queen[MAXQUEEN];  
  9. int total_solution;  /*計算共有幾組解*/
  10. /* 測試在(row,col)上的皇后是否遭受攻擊若遭受攻擊則返回值為1,否則返回0 */
  11. int attack(int row, int col)  
  12. {  
  13.     int i, atk=FALSE;  
  14.     int offset_row, offset_col;  
  15.     i=0;  
  16.     while(!atk && i<row)  
  17.     {  
  18.         offset_row=ABS(i-row);  
  19.         offset_col=ABS(queen[i]-col);  
  20.         /* 判斷兩皇后是否在同一列,是否在同一對角線 */
  21.         /* 若兩皇后在同列或同對角線,則產生攻擊,atk==TRUE */
  22.         atk = (queen[i] == col) || (offset_row == offset_col);  
  23.         i++;  
  24.     }  
  25.     return atk;  
  26. }  
  27. /* 遞迴放置皇后子程式 */
  28. void place(int q, int n)    
  29. {  
  30.     int i=0;  
  31.     while(i < n)  
  32.     {  
  33.         if(!attack(q, i))           /* 皇后未受攻擊 */
  34.         {  
  35.             queen[q]=i;             /* 儲存皇后所在的列位置 */
  36.             /* 判斷是否找到一組解 */
  37.             if(q == n-1)  
  38.                 total_solution++;   /* 得到一個解 */
  39.             else
  40.                 place(q+1, n);      /* 否則繼續擺下一個皇后 */
  41.         }  
  42.         i++;  
  43.     }  
  44. }  
  45. int main(void)  
  46. {  
  47.     int n;  
  48.     int ans[MAXQUEEN+1], i;  
  49.     // 因為“Time Limit Exceeded”,只好先打表
  50.     for(i=1; i<=MAXQUEEN; i++) {  
  51.         // 皇后遍歷
  52.         total_solution = 0;  
  53.         place(0, i);                /*從第0個皇后開始擺放至棋盤*/
  54.         ans[i] = total_solution;