回溯法·遞歸 - 八皇后問題 Eight queens puzzle


問題描述:

在8X8格的國際象棋棋盤上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。

 

問題引申:

在n*n格的國際象棋棋盤上擺放n個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。

 

算法思路:

實現一個逐行逐行地放置皇后的函數

在第X行中的一格放上一個皇后

判斷這個皇后和前幾行的皇后等否互相攻擊,如果能則拿起來重新放到下一列,直到不會和前幾行的皇后相互攻擊后,再轉到X+1行繼續放皇后

然后遞歸調用該函數來解該問題。

 

最初我考慮的是建立一個n*n的數組表示一個棋盤,每個元素代表一個方格,通過不同的值來表示格子有沒有放皇后和會不會被其他皇后攻擊。

后來發現其實完全沒有必要那么復雜_(:з」∠)_。只需要用一維數組就能表示所有皇后的的位置:

int Queen[MAX];

例如Queen[index],index的值表示行數,Queen[index]的值表示列數,那么Queen[3] = 4就表示第三行、第四列有一個皇后。那么通過判斷Queen[i]和Queen[j]的值是否相等就能知道i,j兩個皇后是不是在同一列了。

 

那么又該怎么判斷i和j兩個皇后是不是在同一斜線上呢?

我想到了中學學到的一元一次函數。

y = a * x + b

a是斜率,b是截距。很容易就知道棋盤中的斜線的斜率全都是1或-1,那么判斷兩個皇后是不是在同一斜線上,只需要判斷b是否相等就行了。

斜率為1時 b = y - x,為-1時, b = y + x 。我用兩個數組分別記錄1和-1的情況:

int PlusB[MAX]; int MinusB[MAX];

Queen[index] = i;
PlusB[index] = index - i;
MinusB[index] = index + i;

那么很容易地,通過判斷PlusB[i]和PlusB[j],MinusB[i]和MinusB[j]是否相等就能知道i,j兩個皇后是不是在同一條斜線上了。

 

如此,就能得到判斷一個皇后會不會和前幾行的皇后相互攻擊的函數了:

 

bool Attack( int index ) { for (int i = 0; i < index; i++) if (Queen[i] == Queen[index] || PlusB[i] == PlusB[index] || MinusB[i] == MinusB[index]) return true; return false; }

 

 

那么遞歸調用的放置皇后的函數也很輕松地就實現了:

void PutQueen( int index ) { if (index == n) { Result++; return; } for (int i = 0; i < n; i++) { Queen[index] = i; PlusB[index] = index - i; MinusB[index] = index + i; if (!Attack( index )) PutQueen( index + 1 ); } }

通過index表示行數,即在第index行上放置皇后,每次判斷皇后會否互相攻擊,若不會,則index+1后遞歸調用函數,如果會攻擊則換一列放置皇后。直到index == n,則給結果加一並回溯一步換一列放置皇后,或者試完第index行的所有列了,又會回溯到上一步。

 

整個程序的代碼如下:

 1 #include <fstream>
 2 
 3 using namespace std;  4 
 5 int n = 0;            // Number of Queens
 6 int Result = 0;  7 const int MAX = 100;  8 int Queen[MAX];  9 int PlusB[MAX]; 10 int MinusB[MAX]; 11 FILE *fp; 12 
13 void OutPut( ); 14 bool Attack( int index ); 15 void PutQueen( int index ); 16 
17 int main() 18 { 19     printf( "Input the number of queens: " ); 20     scanf( "%d", &n ); 21     fp = fopen( "Result.txt", "w" ); 22     int index = 0; 23     if (n >= 4 && n <= 100) 24  PutQueen( index ); 25     else
26         printf( "Out of Range!\n" ); 27  fclose( fp ); 28     printf( "The %d queens puzzle has %d distinct solutions.\n", n, Result ); 29     return 0;
30 } 31 32 void OutPut()    // Output to files 33 { 34 for (int i = 0; i < n; i++) 35 { 36 for (int j = 0; j < Queen[i]; j++) 37 fprintf( fp, " %d", 0 ); 38 fprintf( fp, " %d", 1 ); 39 for (int k = Queen[i] + 1; k < n; k++) 40 fprintf( fp, " %d", 0 ); 41 fprintf( fp, "\n" ); 42 } 43 fprintf( fp, "\n" ); 44 } 45 46 bool Attack( int index ) 47 { 48 for (int i = 0; i < index; i++) 49 if (Queen[i] == Queen[index] || PlusB[i] == PlusB[index] || MinusB[i] == MinusB[index]) 50 return true; 51 return false; 52 } 53 54 void PutQueen( int index ) 55 { 56 if (index == n) 57 { 58 Result++; 59 OutPut(); 60 return; 61 } 62 for (int i = 0; i < n; i++) 63 { 64 Queen[index] = i; 65 PlusB[index] = index - i; 66 MinusB[index] = index + i; 67 if (!Attack( index )) PutQueen( index + 1 ); 68 } 69 }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM