八皇后问题是个历史挺悠久的问题,马克斯·贝瑟尔于1848年提出,具体参照百度百科的说明:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
在解决这一问题上回溯法变得很有用,回溯法形象点说有点像走迷宫,当这条路走不通时,退回上一次的状态,寻找另一个方向继续走下去。
在程序设计过程中,过程型语言讲究模块化设计,对象型语言则主张一切皆对象,愚以为二者并非完全对立的两种状态,准确点说应该是,在将一切对象化的过程,问题的解决要模块化,以八皇后问题为例,我们可以设计一个EightQueen类,此为对象化,而EightQueen类内部的问题解决过程则要遵循模块化,解决八皇后问题首先我们需要一个可以判断在某个位置放皇后是否符合条件的函数,然后还需要可以进行回溯于递归的函数,如果还想动态化地演示过程,则还需要一个输出函数,这就是模块化的过程。(这段纯属废话了)
进入正题,判断皇后都不在同意列上很简单,判断不在同一对角线上也不难,不过分为两种情况:正斜对角线和反斜对角线,判断条件分别为:(r1+c1)==(r2+c2),(r1-c1)==(r2-c2),知道了判断条件,就可以进行函数设计了(我定义了一个全局数组queen用来记录每个皇后的位置,还定义了全局变量_count用于记录所有情况的总数,但我并没有将棋盘可以翻转这个因素考虑进代码,所以最后可能会出现一种摆法转个90度就与另一种摆法相同的结果,在这里不直接用count而用_count的原因是命名空间std里有count的定义了,编译会报错):
1 bool Is_meet(int row,int col) { 2 for (int i = 0;i < row;i++) { 3 if (col == queen[i]) 4 return 0; 5 if ((row + col) == (i + queen[i])) 6 return 0; 7 if ((row - col) == (i - queen[i])) 8 return 0; 9 } 10 queen[row] = col; 11 return 1; 12 }
接下来是承当回溯与递归职能的函数的设计,比较好理解,就不进行过多解释:
1 void findQueenPosition(int row) { 2 for (int j_col = 0;j_col < 8;j_col++) { //j_col变量用于遍历每一列 3 if (Is_meet(row, j_col)) { 4 if (row == 7) { //如果已经是最后一行了就可以退出循环 5 break; 6 } 7 else { 8 findQueenPosition(row + 1); //进行递归 9 } 10 } 11 } 12 queen[row] = -1; 13 return; 14 }
为了在控制台动态的演示出过程,我们需设计一下display()函数,在显示过程中为了让我们可以捕捉到变化,我们需调用Sleep()库函数将停留时间变长,Sleep()库函数调用需包含头文件<window.h>,同时也为了实现动态,我们要将旧的输出清零,这就需要用到system("cls")语句,该语句可以将控制台所有输出清零,代码如下:
1 void display() { 2 system("cls"); 3 cout << "八皇后问题动态演示:" << endl; 4 for (int i = 0;i < 8;i++) { 5 if (queen[i] == -1) { 6 for (int j = 0;j < 8;j++) 7 cout << ". "; 8 cout << endl; 9 } 10 else { 11 for (int j = 0;j < queen[i];j++) { 12 cout << ". "; 13 } 14 cout << "# "; 15 for (int k = queen[i] + 1;k < 8;k++) { 16 cout << ". "; 17 } 18 cout << endl; 19 } 20 } 21 Sleep(100);//停留时间可以自行更改 22 }
既然加入了输出函数,那findQueenPosition()函数也要做出些许变化,修改完如下:
1 void findQueenPosition(int row) { 2 for (int j_col = 0;j_col < 8;j_col++) { 3 if (Is_meet(row, j_col)) { 4 if (row == 7) { 5 display(); 6 cout << "Case " << ++_count << "." << endl; 7 Sleep(3000);//停留时间可以自行更改 8 break; 9 } 10 else { 11 display(); 12 findQueenPosition(row + 1); 13 } 14 } 15 } 16 queen[row] = -1; 17 return; 18 }
到这里这个程序就基本完成了,为了便于运行,我将完整代码贴在下面:
1 #include <iostream> 2 #include <windows.h> 3 using namespace std; 4 5 int queen[8]={-1,-1,-1,-1,-1,-1,-1,-1}; 6 int _count = 0; 7 8 bool Is_meet(int row,int col) { 9 for (int i = 0;i < row;i++) { 10 if (col == queen[i]) 11 return 0; 12 if ((row + col) == (i + queen[i])) 13 return 0; 14 if ((row - col) == (i - queen[i])) 15 return 0; 16 } 17 queen[row] = col; 18 return 1; 19 } 20 21 void display() { 22 system("cls"); 23 cout << "八皇后问题动态演示:" << endl; 24 for (int i = 0;i < 8;i++) { 25 if (queen[i] == -1) { 26 for (int j = 0;j < 8;j++) 27 cout << ". "; 28 cout << endl; 29 } 30 else { 31 for (int j = 0;j < queen[i];j++) { 32 cout << ". "; 33 } 34 cout << "# "; 35 for (int k = queen[i] + 1;k < 8;k++) { 36 cout << ". "; 37 } 38 cout << endl; 39 } 40 } 41 Sleep(100);//停留时间可以自行更改 42 } 43 44 void findQueenPosition(int row) { 45 for (int j_col = 0;j_col < 8;j_col++) { 46 if (Is_meet(row, j_col)) { 47 if (row == 7) { 48 display(); 49 cout << "Case " << ++_count << "." << endl; 50 Sleep(3000);//停留时间可以自行更改 51 break; 52 } 53 else { 54 display(); 55 findQueenPosition(row + 1); 56 } 57 } 58 } 59 queen[row] = -1; 60 return; 61 } 62 63 int main() { 64 findQueenPosition(0); 65 cout << "The total case of eight queen problem is " << _count << endl; 66 return 0; 67 }
经过简单的测试,代码应该是没问题了,但仍可能会有错误出现,欢迎指正。