下面是算法的高级伪码描述,这里用一个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); }