首先来看看这张模拟八皇后的图。
下面这张黑色背景是其中一个方案的截图,第一行代表皇后的坐标;后面的是棋盘,其中*是边界,空格是空区,#是皇后。
#include <iostream> #include <cstring> #include "DTString.h" #include "LinkList.h" // 这里使用链表存储皇后位置 using namespace std; using namespace DTLib; template <int SIZE> // N皇后问题,SIZE表示皇后个数或者棋盘大小 class QueenSolution : public Object { protected: enum { N = SIZE + 2 }; // 为了边界识别,棋盘四周都要加一格 struct Pos : public Object // 方位结构体 { Pos(int px = 0, int py = 0) : x(px), y(py) { } int x; int y; }; int m_chessboard[N][N]; // 棋盘,0表示空位,1表示皇后,2表示边界 Pos m_direction[3]; // 方向-1,-1表示左斜线;0,-1表示下方;1,-1表示右斜线;首先从最下方开始,所以只需考虑前面的行。 LinkList<Pos> m_solution; // 用链表记录解决方案 int m_count; // 记录有效方案数量 void init() { m_count = 0; // 有效方案初始化为0 for(int i=0; i<N; i+=(N-1)) // 遍历第0行和最后一行 { for(int j=0; j<N; j++) // 遍历每一列 { m_chessboard[i][j] = 2; // 给棋盘的上下设置边界 m_chessboard[j][i] = 2; // 给棋盘的左右设置边界 } } for(int i=1; i<=SIZE; i++) // 初始化棋盘为空位 { for(int j=1; j<=SIZE; j++) { m_chessboard[i][j] = 0; } } m_direction[0].x = -1; // 初始化方向数据 m_direction[0].y = -1; m_direction[1].x = 0; m_direction[1].y = -1; m_direction[2].x = 1; m_direction[2].y = -1; } void print() // 打印有效方案,方案记录了坐标值 { for(m_solution.move(0); !m_solution.end(); m_solution.next()) // 打印坐标 { cout << "(" << m_solution.current().x << ", " << m_solution.current().y << ")" ; } cout << endl; // 坐标打印换行 for(int i=0; i<N; i++) // 打印棋盘 { for(int j=0; j<N; j++) { switch(m_chessboard[i][j]) { case 0: cout << " "; break; // 空位 case 1: cout << "#"; break; // 皇后 case 2: cout << "*"; break; // 边界 } } cout << endl; } cout << endl; // 棋盘打印完换行 } bool check(int x, int y, int d) // 检查是否可放置皇后 { bool flag = true; do { x += m_direction[d].x; y += m_direction[d].y; flag = flag && (m_chessboard[x][y] == 0);// 查看坐标位置是否有空位 } while( flag ); // 本次循环后不会再有空位 return (m_chessboard[x][y] == 2); // 返回真就是到边界,否则就是有皇后 } void run(int j) // 检查当前行有没有可放置皇后的位置 { if( j <= SIZE ) // 检查当前行在棋盘内,注意不要跑到边界上 { for(int i=1; i<=SIZE; i++) { if( check(i, j, 0) && check(i, j, 1) && check(i, j, 2) ) // 如果if为真表示可以放置皇后 { m_chessboard[i][j] = 1; // 标记该处有皇后 m_solution.insert(Pos(i, j)); // 记录皇后的位置到链表 run(j + 1); // 递归判断下一行 m_chessboard[i][j] = 0; // 回溯后要把棋盘当前位置的皇后清除,避免下一次调用时还有上次的皇后位置 m_solution.remove(m_solution.length() - 1); // 回溯后记录皇后位置的链表长度也要减少 } } } else // 如果j大于SIZE就表示一轮检查结束,方案计数加1并打印方案 { m_count++; print(); } } public: QueenSolution() { init(); } void run() { run(1); // 从第一行开始,注意边界占用的行数(本例四周都占用了1行或列记录边界,所以从1开始) cout << "Total: " << m_count << endl; // 输出方案个数 } }; int main() { QueenSolution<8> qs; qs.run(); return 0; }