下面是算法的高級僞碼描述,這裏用一個N*N的矩陣來存儲棋盤:ios
1) 算法開始, 清空棋盤,當前行設爲第一行,當前列設爲第一列算法
2) 在當前行,當前列的位置上判斷是否知足條件(即保證通過這一點的行,列與斜線上都沒有兩個皇后),若不知足,跳到第4步數組
3) 在當前位置上知足條件的情形:bash
在當前位置放一個皇后,若當前行是最後一行,記錄一個解;數據結構
若當前行不是最後一行,當前行設爲下一行, 當前列設爲當前行的第一個待測位置;函數
若當前行是最後一行,當前列不是最後一列,當前列設爲下一列;spa
若當前行是最後一行,當前列是最後一列,回溯,即清空當前行及如下各行的棋盤,而後,當前行設爲上一行,當前列設爲當前行的下一個待測位置;設計
以上返回到第2步code
4) 在當前位置上不知足條件的情形:排序
若當前列不是最後一列,當前列設爲下一列,返回到第2步;
若當前列是最後一列了,回溯,即,若當前行已是第一行了,算法退出,不然,清空當前行及如下各行的棋盤,而後,當前行設爲上一行,當前列設爲當前行的下一個待測位置,返回到第2步;
數據結構:在N*N的矩陣中,設皇后與列對應,擺放皇后,便是要找到皇后對就的行。i 表示第幾個皇后,queue[i]表求皇后所在的行。
1.存放第 i 個皇后:從第1行到N行,對每一行進行試探,每次試探檢查是否符合要求,
2.若符合要求,則檢查是否爲最後個皇后(即最後一列)
3.如果,則打印出全部皇后對應的位置座標。若不是最後一個則對下皇后進行找位,即返回到1處再執行
/*遞歸方法的一種實現*/ #include <stdio.h> #include <stdlib.h> #define max 8 int queen[max], sum=0; /* max爲棋盤最大座標 ,sum 計數輸入的方案數量*/ void show() {/* 輸出全部皇后的座標 */ int i; for(i = 0; i < max; i++){ printf("(%d,%d) ", i, queen[i]); } printf("\n"); sum++; } int check(int n) {/* 檢查當前列可否放置皇后 */ int i; for(i = 0; i < n; i++){ /* 檢查橫排和對角線上是否能夠放置皇后 */ if(queen[i] == queen[n] || abs(queen[i] - queen[n]) == (n - i)) { return 0; } } return 1; } void put(int n) {/* 回溯嘗試皇后位置,n爲第N個皇后 */ int i; for(i = 0; i < max; i++){ queen[n] = i; /* 將皇后擺到當前循環到的位置 */ if(check(n)){ if(n == max - 1){ show(); /* 若是所有擺好,則輸出全部皇后的座標 */ } else{ put(n + 1); /* 不然繼續擺放下一個皇后 */ } } } } int main() { put(0); /* 從第0個皇后開始放起 */ printf("%d", sum); system("pause"); return 0; }
pause腳本以下
#!/bin/bash echo 按任意鍵繼續 read -n 1 echo 繼續運行
chmod a+x pause
sudo cp pause /usr/bin/.
可是通常來講遞歸的效率比較差,下面重點討論一下該問題的非遞歸實現。
非遞歸方法的一個重要問題時什麼時候回溯及如何回溯的問題。程序首先對N行中的每一行進行探測,尋找該行中能夠放置皇后的位置,具體方法是對該行的每一列進行探測,看是否能夠放置皇后,若是能夠,則在該列放置一個皇后,而後繼續探測下一行的皇后位置。若是已經探測完全部的列都沒有找到能夠放置皇后的列,此時就應該回溯,把上一行皇后的位置日後移一列,若是上一行皇后移動後也找不到位置,則繼續回溯直至某一行找到皇后的位置或回溯到第一行,若是第一行皇后也沒法找到能夠放置皇后的位置,則說明已經找到全部的解程序終止。若是該行已是最後一行,則探測完該行後,若是找到放置皇后的位置,則說明找到一個結果,打印出來。可是此時並不能再此處結束程序,由於咱們要找的是全部N皇后問題全部的解,此時應該清除該行的皇后,從當前放置皇后列數的下一列繼續探測。
/** * 回溯法解N皇后問題 * 使用一個一維數組表示皇后的位置 * 其中數組的下標表示皇后所在的行 * 數組元素的值表示皇后所在的列 * 這樣設計的棋盤,全部皇后一定不在同一行,因而行衝突就不存在了 * date : 2011-08-03 * author: liuzhiwei **/ #include <stdio.h> #include <stdlib.h> #include <math.h> #define QUEEN 8 //皇后的數目 #define INITIAL -10000 //棋盤的初始值 int a[QUEEN]; //一維數組表示棋盤 void init() {//對棋盤進行初始化 int *p; for (p = a; p < a + QUEEN; ++p) { *p = INITIAL; } } int valid(int row, int col) { //判斷第row行第col列是否能夠放置皇后 int i; for (i = 0; i < QUEEN; ++i) { //對棋盤進行掃描 if (a[i] == col || abs(i - row) == abs(a[i] - col)) //判斷列衝突與斜線上的衝突 return 0; } return 1; } void print() { //打印輸出N皇后的一組解 int i, j; for (i = 0; i < QUEEN; ++i) { for (j = 0; j < QUEEN; ++j) { if (a[i] != j) //a[i]爲初始值 printf("%c ", '.'); else //a[i]表示在第i行的第a[i]列能夠放置皇后 printf("%c ", '#'); } printf("\n"); } for (i = 0; i < QUEEN; ++i) printf("%d ", a[i]); printf("\n"); printf("--------------------------------\n"); } void queen(){ //N皇后程序 int n = 0; int i = 0, j = 0; while (i < QUEEN){ while (j < QUEEN) { //對i行的每一列進行探測,看是否能夠放置皇后 if(valid(i, j)) { //該位置能夠放置皇后 a[i] = j; //第i行放置皇后 j = 0; //第i行放置皇后之後,須要繼續探測下一行的皇后位置,因此此處將j清零,從下一行的第0列開始逐列探測 break; }else{ ++j; //繼續探測下一列 } } if(a[i] == INITIAL) { //第i行沒有找到能夠放置皇后的位置 if ( 0 == i) //回溯到第一行,仍然沒法找到能夠放置皇后的位置,則說明已經找到全部的解,程序終止 break; else { //沒有找到能夠放置皇后的列,此時就應該回溯 --i; j = a[i] + 1; //把上一行皇后的位置日後移一列 a[i] = INITIAL; //把上一行皇后的位置清除,從新探測 continue; } } if (i == QUEEN - 1) { //最後一行找到了一個皇后位置,說明找到一個結果,打印出來 printf("answer %d : \n", ++n); print(); //不能在此處結束程序,由於咱們要找的是N皇后問題的全部解,此時應該清除該行的皇后,從當前放置皇后列數的下一列繼續探測。 //_sleep(600); j = a[i] + 1; //從最後一行放置皇后列數的下一列繼續探測 a[i] = INITIAL; //清除最後一行的皇后位置 continue; } ++i; //繼續探測下一行的皇后位置 } } int main(void){ init(); queen(); system("pause"); return 0; }
利用STL庫
#include <cmath> #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAX = 8; vector<int> board(MAX); void show_result(){ for(size_t i = 0; i < board.size(); i++) cout<<"("<<i<<","<<board[i]<<")"; cout<<endl; } int check_cross(){ for(size_t i = 0; i < board.size()-1; i++){ for(size_t j = i+1; j < board.size(); j++){ if((j-i) == (size_t)abs(board[i]-board[j])) return 1; } } return 0; } void put_chess(){ while(next_permutation(board.begin(), board.end())){ if(!check_cross()) show_result(); } } int main(){ for(size_t i =0; i < board.size(); i++) board[i] = i; put_chess(); return 0; }
回溯法
#include <cstdio> static int count=0; void print(int (*chess)[8],int row) { for(int i=0;i<row;i++) { for(int j=0;j<8;j++) printf("%d ",chess[i][j]); printf("\n"); } return; } bool notDanger(int row,int col,int (*chess)[8]) { int flag1=0,flag2=0,flag3=0,flag4=0,flag5=0,flag6=0; for(int i=0;i<8;i++) { if(chess[i][col]) { flag1=1; break; } } for(int i=0;i<8;i++) { if(chess[row][i]) { flag2=1; break; } } for(int i=row,j=col;i<8&&j<8;i++,j++) { if(chess[i][j]) { flag3=1; break; } } for(int i=row,j=col;i<8&&j>=0;i++,j--) { if(chess[i][j]) { flag4=1; break; } } for(int i=row,j=col;i>=0&&j>=0;i--,j--) { if(chess[i][j]) { flag5=1; break; } } for(int i=row,j=col;i>=0&&j<8;i--,j++) { if(chess[i][j]) { flag6=1; break; } } if(flag1||flag2||flag3||flag4||flag5||flag6) return false; else return true; } void eightQueen(int row,int col,int (*chess)[8]) { int chesstmp[8][8]; for(int i=0;i<8;i++) for(int j=0;j<8;j++) chesstmp[i][j]=chess[i][j]; if(8==row) { count++; printf("the %d answer\n",count); print(chess,8); printf("-------------\n"); } else { for(int i=0;i<col;i++) { if(notDanger(row,i,chess)) { for(int j=0;j<8;j++) { chesstmp[row][j]=0; } chesstmp[row][i]=1; eightQueen(row+1,col,chesstmp); } } } return; } int main() { int chess[8][8]={0}; eightQueen(0,8,chess); }
#include <stdio.h> #include <stdlib.h> #define N 8 #define FALSE 0 #define TRUE 1 static int x = 0; //回溯法遞歸實現八皇后問題 void//輸出棋盤 printChessboard(int const *flag){ int chessboard[N][N] = {0}; for ( int i=0; i<N; i++) { chessboard[i][flag[i]] = TRUE; } for ( int i=0; i<N; i++) { for ( int j=0; j<N; j++) { printf( "%d ", chessboard[i][j]); } printf( "\n"); } } int checkQueen(int const *flag, int p){ int flag_t = TRUE; int pv = flag[p]; if ( p==0) { return flag_t; } p--; for ( int i=1; p>=0; p--, i++) { if ( flag[p]==pv || flag[p]==pv-i || flag[p]==pv+i) { flag_t = FALSE; } } return flag_t; } void eightQueen(int *flag, int p){ for ( int i=0; i<N; i++) { flag[p] = i; if ( checkQueen( flag, p)==TRUE && p==7) { printChessboard( flag); x++; printf( "%d\n", x); } else if ( checkQueen( flag, p ) == TRUE){ eightQueen( flag, p+1); } } } int main(int argc, const char * argv[]) { int flag[N] = {-1, -1, -1, -1, -1, -1, -1, -1}; // int flag[N] = {3, 2, 1, 5, 6, 5, 2, 3}; eightQueen( flag, 0); // printChessboard( flag); return 0; }
8皇后問題擴展:如下代碼根據用戶輸入的棋盤格數,打印出全部的解法,輸入n=8時,即爲8*8格的棋盤。核心代碼參考自劉汝佳的《算法競賽入門經典》,完整代碼以下,只要保存爲.c文件,編譯執行便可:
#include <stdio.h> #define MAXN 20 //打印函數 void printf_count(int *A,int n) { for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { if(A[i]==j) printf("#"); else printf("*"); } printf("\n"); } } //遞歸函數 int count; void search(int n,int *A,int cur) { int i,j,ok; if(n==cur)//遞歸邊界:最後一行也排序好了,全部皇后不衝突 { count++;//可行方案數+1 printf_count(A,n);//打印一個可行方案 printf("\n"); }else{ for(i=0;i<n;i++)//在cur這一行嘗試全部0至n-1的位置 { ok=1; A[cur]=i; for(j=0;j<cur;j++)//與前cur-1行進行檢查是否衝突 { if(A[cur]==A[j]||cur-A[cur]==j-A[j]||cur+A[cur]==j+A[j])//若是在同一列、或兩個斜線上,則此位置衝突 { ok=0;break; } } if(ok)search(n,A,cur+1);//i的位置可用,遞歸調用,設置下一行皇后的位置 } } } void main() { int n,A[MAXN]; printf("請輸入棋盤的行列數,一般爲8\n"); scanf("%d",&n);//輸入棋盤行(列)數 search(n,A,0); printf("%d\n",count); }