学校数据结构的课程实验之一。
数据结构:(其实只用了一个二维数组)
算法:深度优先搜索,试探回溯
需求分析:
设计一个在控制台窗口运行的“n皇后问题”解决方案生成器,要求实现以下功能:
由n*n个方块排成n行n列的正方形称为n元棋盘。如果两个皇后位于n元棋盘上的同一行、同一列或同一对角线上,则称它们在互相攻击。现要找出使棋盘上n个皇后互不攻击的布局。
编制程序解决上述问题,以n=6运行程序,输出结果。
算法解释:
首先试探当前行第一个可用的位置(列、对角线没有被占领),摆放皇后之后,试探下一行的第一个可用位置;如果遇到此行没有可用位置,则返回上一行,移除上一行的皇后,试探此行的下一个可用位置,直至n个皇后全部摆放好,生成一种方案。
主函数:
1 int main() 2 /*Pre: The user enters a valid board size. 3 Post: All solutions to the n-queens puzzle for the selected board size 4 are printed. 5 Uses: The class Queens and the recursive functionsolve from. */ 6 { 7 int board_size; 8 char choice = 'y'; 9 char enter; 10 while (choice == 'y')//由用户决定是否继续 11 { 12 sum = 0; 13 cout << "What is the size of the board?"<<flush; 14 cin >> board_size; 15 if(board_size < 0||board_size > max_board) 16 cout<<"The number must between 0 and "<<max_board<<endl; 17 else 18 { 19 Queens configuration(board_size);//创建size*size的对象 20 21 cout << "there is total " << solve_from(configuration) << " configurations." << endl;//打印所有解决方案和方案个数 22 } 23 cout << endl << "Would you like to continue? [y/n]" << endl; 24 //回车符的消去 25 fflush(stdin); 26 while ((enter = cin.get()) == '\n') 27 { 28 enter = cin.get(); 29 } 30 cin.putback(enter); 31 cin >> choice;//移植了计算器的代码 32 } 33 return 0; 34 }
辅助函数(计算出所有解决方案)(参考了经典教材"Data Structures and Program Design in C++" Robert L. Kruse, Alexander J. Ryba 高等教育出版社-影印版)
1 int sum = 0;//记录解决方案个数 2 3 int solve_from(Queens &configuration)//通过递归、回溯找到所有解决方案并打印 4 /*Pre: the queens configuration represents a partially completed 5 arrangement of nonattacking queens on a chessboard. 6 Post: all n-queens solutions that extend the given configuration are 7 printed. The configuration is restored to its initial state. 8 Uses: the class Queens and the function solve_from, recursively.*/ 9 { 10 if (configuration.is_solved())//当生成一种解决方案时打印,sum自增一 11 { 12 sum++; 13 configuration.print(); 14 cout <<"================" <<endl; 15 } 16 else 17 for(int col=0; col<configuration.board_size; col++) 18 if(configuration.unguarded(col)) 19 { 20 configuration.insert(col); 21 solve_from(configuration);//recursively continue to add queens当生成一种解决方案时打印 22 configuration.remove(col);//return the last row and the last col.试探上一层的下一种方案(无论上一次试探是成功还是失败) 23 } 24 return sum; 25 }
注:
每次回溯其实有两种可能:“摆放满了n个皇后”或者“此行没有可放的位置”,二者都会返回上一行去试探下一种可能,只不过摆满n个皇后的情况会生成一种方案(被if截获,回到上一层循环),生成后还是回到倒数第二行再进行试探。因此一次深度优先搜索(调用一次solve_from函数)可以将所有方案全部输出。
“皇后”类的定义
1 const int max_board = 15;//最大棋盘阶数 2 using namespace std; 3 4 class Queens 5 { 6 public: 7 Queens(int size); 8 bool is_solved() const;//判断是否完成一种方案 9 void print() const;//打印当前方案 10 bool unguarded(int col) const;//判断某格是否可放皇后 11 void insert(int col);//摆放皇后 12 void remove(int col);//移除 13 int board_size;//dimension of board = maximum number of queens 14 private: 15 int count;//current number of queens = first unoccupied row 16 bool queen_square[max_board][max_board];//存放棋盘状态的二维数组 17 };
“皇后”类的实现(同样参考了经典教材"Data Structures and Program Design in C++" Robert L. Kruse, Alexander J. Ryba 高等教育出版社-影印版)
1 #include <iostream> 2 #include "Queens.h" 3 4 Queens::Queens(int size) 5 { 6 board_size = size; 7 count = 0;//从第一行开始计数 8 for(int row=0; row<board_size; row++) 9 for(int col=0; col<board_size; col++) 10 queen_square[row][col] = false;//生成size*size的空棋盘 11 } 12 13 bool Queens::is_solved() const 14 /*whether the number of queens already placed 15 equals board_size*/ 16 { 17 bool solved = false; 18 if(count == board_size)//当board_size个皇后都摆放完毕时,生成一种方案 19 solved = true; 20 return solved; 21 } 22 23 void Queens::print() const 24 { 25 for (int row = 0; row < board_size; row++) 26 { 27 for (int col = 0; col < board_size; col++) 28 if (queen_square[row][col] == true) 29 cout << "* "; 30 else 31 cout << "_ ";//逐个打印棋盘元素,有皇后打印'*',无皇后打印'_' 32 cout << endl; 33 } 34 } 35 36 bool Queens::unguarded(int col) const 37 /*Post: Return true or false according as the square in the first 38 unoccupied row(row count) and colum col is not guarded by andy queen*/ 39 { 40 int i; 41 bool ok = true;//turn false if we find a queen in column or diagonal 42 for(i=0; ok && i < count; i++) 43 ok = !queen_square[i][col];//check upper part of column同列 44 for(i=1; ok && count-i >= 0 && col-i >=0; i++) 45 ok = !queen_square[count-i][col-i];//check upper-left diagonal 46 for(i=1; ok && count-i >= 0 && col+i < board_size; i++) 47 ok = !queen_square[count-i][col+i];//chekck upper-right diagonal 48 return ok; 49 } 50 51 void Queens::insert(int col) 52 /*Pre: The square in the first unoccupied row(row count) and column is not 53 guarded by any queen. 54 Post: A queen has been inserted into the square at row count and column col; 55 count has been incremented by 1*/ 56 { 57 queen_square[count++][col] = true;//放入皇后,计数器自增一(到下一行) 58 } 59 60 void Queens::remove(int col) 61 /*Pre: there is a queen in the square in row count-1 and column col. 62 Post: the above queen has been removed; count has been decremented by 1.*/ 63 { 64 queen_square[count-1][col] = false;//移出皇后,计数器自减一(回上一行) 65 count--; 66 }
运行截图:
注:
当输入的棋盘阶数比较大(如:8)时,命令行窗口的缓冲区默认300行可能会不够显示,所以要在属性修改“高度”,使所有结果都显示出来。