#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 }; // N表示棋盤大小,爲了邊界識別,棋盤四周都要加一格 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]; // 共3個方向;方向-一、-1表示左斜線;0、-1表示下方;一、-1表示右斜線;首先從最下方開始,因此只需考慮下面的行。 LinkList<Pos> m_solution; // 用鏈表記錄解決方案,注意該鏈表只記錄一個方案,而不是所有 int m_count; // 記錄有效方案數量 void init() // 初始化函數 { m_count = 0; // 有效方案初始化爲0 for(int i=0; i<N; i+=(N-1)) // 設置棋盤邊界,遍歷第1行和最後一行 { for(int j=0; j<N; j++) // 遍歷每一列 { m_chessboard[i][j] = 2; // 給棋盤的上下設置邊界 m_chessboard[j][i] = 2; // 給棋盤的左右設置邊界 } } for(int i=1; i<=SIZE; i++) // 初始化棋盤爲空位,注意0是邊界,因此從1開始 { 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) // 檢查是否可放置皇后(xy是假定要放置皇后的座標,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); // 判斷do循環以後的xy值是否到邊界,返回真就是到邊界可放置皇后,不然就是有皇后不能放置 } 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) ) // 檢查當前位置是否可放置皇后,須要判斷3個方向 { 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; }
本例來自於狄泰《數據結構》45課第3節整理。ios