8皇后问题详细过程

八皇后问题

在一个8x8的棋盘上,每行每列防止棋子“皇后”,”皇后“不能在同一行,同一列同一条斜线上。
这里写图片描述
对于这个问题通常的做法是:
1. 在一行第一个位置放置一个
2. 遍历第二行,如果找到第一个可放置的位置,则放置,遍历下一行。否则跳转到上一行遍历下一个位置。
3. 一直重复直到所有的位置都被遍历完。
为了便于说明,下面使用4皇后说明。

四皇后坐标
00010203
10111213
20212223
30313233
22坐标的正对角线坐标和
0
2
22
6
22坐标的负对角线坐标差(x-y)
-2
22
2

可以得出:正对角线坐标和范围为0~(n-1)*2(2为皇后数量),总个数为2n-1,负对角线的差范围-(n-1)~(n-1),总个数为2n-1个。.
对于4皇后问题的解的完整处理过程如下:
这里写图片描述
这里写图片描述
这里写图片描述
代码实现:
n=4 副对角线上的元素为-3~3 为了能处理负数,统一给代码加上n-1,详情查看代码rightDiagonal[row-col+norm]

#include <iostream>
using namespace std;
class ChessBoard{
private:
    const bool available;
    const int squares,norm;                     //norm为了将副对角线差变换到非负数
    bool *column,*leftDiagonal,*rightDiagonal;
    int *positionInRow,howMany;
    void putQueen(int);
    ostream & printBoard(ostream &);
    void initializeBoard();
public:
    ChessBoard();
    ChessBoard(int);
    ~ChessBoard();
    void findSolution();
};
ChessBoard::~ChessBoard() {
    delete [] positionInRow;
    delete [] column;
    delete [] leftDiagonal;
}
ChessBoard::ChessBoard():available(true),squares(8),norm(squares-1){
    initializeBoard();
}
ChessBoard::ChessBoard(int n):available(true),squares(n),norm(squares-1) {
    initializeBoard();
}
void ChessBoard::initializeBoard() {
    int i;
    column = new bool[squares];             //放置行
    positionInRow = new int[squares];       //放置位置
    leftDiagonal = new bool[squares*2-1];   //左边斜线
    rightDiagonal = new bool [squares*2-1]; //右边斜线
    for(i = 0;i<squares;i++)
        positionInRow[i] = -1;
    for(i=0;i<squares;i++)
        column[i] = available;
    for(i=0;i<squares*2-1;i++)
        leftDiagonal[i] = rightDiagonal[i] = available;
    howMany = 0;
}
void ChessBoard::putQueen(int row) {
    cout<<"函数开始:当前行为: "<<row<<"行,遍历不同的列col"<<endl;
    for (int col = 0; col < squares; col++){
        cout<<"for循环:循环开始在"<<row<<"行,"<<col<<"列!"<<endl;

        if(column[col] == available &&leftDiagonal [row+col] == available &&rightDiagonal[row-col+norm] == available) {
            cout<<"判断:经判断"<<col<<"列能放,列放置位置: "<<"("<<row<<","<<col<<")"<<"进入下一行:"<<row+1<<endl;
            //放置皇后在row
            positionInRow[row] = col;
            column[col] = !available;
            leftDiagonal[row+col] = !available;
            rightDiagonal[row-col+norm] = !available;
            if (row < squares-1)
                putQueen(row+1);
            else{
                printBoard(cout);
                howMany+=1;
                cout<<"找到第"<<howMany<<"个结果"<<endl;
            }
            column[col] = available;
            leftDiagonal[row+col] = available;
            rightDiagonal[row-col+norm] = available;
        }
        if(col==squares-1)
        {
            if(row-1>=0)
                cout<<"当前行遍历完成!跳到上一行"<<row-1<<endl;
        }

    }
}
ostream & ChessBoard::printBoard(ostream &os) {
//    cout<<"当前行为: "<<col<<"\n";
    return os;
}
void ChessBoard::findSolution() {
    putQueen(0);
    cout<<"\n";
    cout<<howMany<<" Solutions found.\n";
}

int main() {
    ChessBoard c1(4);
    c1.findSolution();
    return 0;
}

输出如下:

函数开始:当前行为: 0行,遍历不同的列col
for循环:循环开始在0行,0列!
判断:经判断0列能放,列放置位置: (0,0)进入下一行:1
函数开始:当前行为: 1行,遍历不同的列col
for循环:循环开始在1行,0列!
for循环:循环开始在1行,1列!
for循环:循环开始在1行,2列!
判断:经判断2列能放,列放置位置: (1,2)进入下一行:2
函数开始:当前行为: 2行,遍历不同的列col
for循环:循环开始在2行,0列!
for循环:循环开始在2行,1列!
for循环:循环开始在2行,2列!
for循环:循环开始在2行,3列!
当前行遍历完成!跳到上一行1
for循环:循环开始在1行,3列!
判断:经判断3列能放,列放置位置: (1,3)进入下一行:2
函数开始:当前行为: 2行,遍历不同的列col
for循环:循环开始在2行,0列!
for循环:循环开始在2行,1列!
判断:经判断1列能放,列放置位置: (2,1)进入下一行:3
函数开始:当前行为: 3行,遍历不同的列col
for循环:循环开始在3行,0列!
for循环:循环开始在3行,1列!
for循环:循环开始在3行,2列!
for循环:循环开始在3行,3列!
当前行遍历完成!跳到上一行2
for循环:循环开始在2行,2列!
for循环:循环开始在2行,3列!
当前行遍历完成!跳到上一行1
当前行遍历完成!跳到上一行0
for循环:循环开始在0行,1列!
判断:经判断1列能放,列放置位置: (0,1)进入下一行:1
函数开始:当前行为: 1行,遍历不同的列col
for循环:循环开始在1行,0列!
for循环:循环开始在1行,1列!
for循环:循环开始在1行,2列!
for循环:循环开始在1行,3列!
判断:经判断3列能放,列放置位置: (1,3)进入下一行:2
函数开始:当前行为: 2行,遍历不同的列col
for循环:循环开始在2行,0列!
判断:经判断0列能放,列放置位置: (2,0)进入下一行:3
函数开始:当前行为: 3行,遍历不同的列col
for循环:循环开始在3行,0列!
for循环:循环开始在3行,1列!
for循环:循环开始在3行,2列!
判断:经判断2列能放,列放置位置: (3,2)进入下一行:4
找到第1个结果
for循环:循环开始在3行,3列!
当前行遍历完成!跳到上一行2
for循环:循环开始在2行,1列!
for循环:循环开始在2行,2列!
for循环:循环开始在2行,3列!
当前行遍历完成!跳到上一行1
当前行遍历完成!跳到上一行0
for循环:循环开始在0行,2列!
判断:经判断2列能放,列放置位置: (0,2)进入下一行:1
函数开始:当前行为: 1行,遍历不同的列col
for循环:循环开始在1行,0列!
判断:经判断0列能放,列放置位置: (1,0)进入下一行:2
函数开始:当前行为: 2行,遍历不同的列col
for循环:循环开始在2行,0列!
for循环:循环开始在2行,1列!
for循环:循环开始在2行,2列!
for循环:循环开始在2行,3列!
判断:经判断3列能放,列放置位置: (2,3)进入下一行:3
函数开始:当前行为: 3行,遍历不同的列col
for循环:循环开始在3行,0列!
for循环:循环开始在3行,1列!
判断:经判断1列能放,列放置位置: (3,1)进入下一行:4
找到第2个结果
for循环:循环开始在3行,2列!
for循环:循环开始在3行,3列!
当前行遍历完成!跳到上一行2
当前行遍历完成!跳到上一行1
for循环:循环开始在1行,1列!
for循环:循环开始在1行,2列!
for循环:循环开始在1行,3列!
当前行遍历完成!跳到上一行0
for循环:循环开始在0行,3列!
判断:经判断3列能放,列放置位置: (0,3)进入下一行:1
函数开始:当前行为: 1行,遍历不同的列col
for循环:循环开始在1行,0列!
判断:经判断0列能放,列放置位置: (1,0)进入下一行:2
函数开始:当前行为: 2行,遍历不同的列col
for循环:循环开始在2行,0列!
for循环:循环开始在2行,1列!
for循环:循环开始在2行,2列!
判断:经判断2列能放,列放置位置: (2,2)进入下一行:3
函数开始:当前行为: 3行,遍历不同的列col
for循环:循环开始在3行,0列!
for循环:循环开始在3行,1列!
for循环:循环开始在3行,2列!
for循环:循环开始在3行,3列!
当前行遍历完成!跳到上一行2
for循环:循环开始在2行,3列!
当前行遍历完成!跳到上一行1
for循环:循环开始在1行,1列!
判断:经判断1列能放,列放置位置: (1,1)进入下一行:2
函数开始:当前行为: 2行,遍历不同的列col
for循环:循环开始在2行,0列!
for循环:循环开始在2行,1列!
for循环:循环开始在2行,2列!
for循环:循环开始在2行,3列!
当前行遍历完成!跳到上一行1
for循环:循环开始在1行,2列!
for循环:循环开始在1行,3列!
当前行遍历完成!跳到上一行0
<think>好的,用户想了解如何用非递归方式解决皇后问题,尤其是算法或代码实现。首先,我需要回顾一下皇后问题的基本要求和递归解法,然后思考如何将其转换为非递归形式。 皇后问题要求在8x8的棋盘上放置皇后,使得它们互不攻击。这意味着每行、每列、每条主对角线和次对角线上都只有一个皇后递归解法通常使用回溯,逐行尝试放置皇后,并在冲突时回溯到上一行调整位置。 用户需要非递归的实现,这通常需要手动模拟递归的栈结构。递归中的栈保存了函数调用的状态,包括当前行、列的位置以及已放置皇后的信息。非递归算法需要用显式的栈来保存这些状态,并在循环中处理回溯逻辑。 接下来,我需要参考用户提供的引用资料。引用[3]提到非递归算法使用栈结构模拟递归,且需要注意记录下一次迭代的起点。引用[4]提到使用数组来记录列、主对角线和次对角线的占用情况。这些信息对设计非递归算法非常重要。 设计步骤可能包括: 1. 初始化栈,从第0行开始尝试放置皇后。 2. 对于每一行,从当前列开始寻找可放置的位置。 3. 如果找到合法位置,记录列、对角线状态,并将下一行压入栈。 4. 如果无法找到,弹出栈顶,回溯到上一行,继续尝试下一个列。 5. 当栈顶的行数达到8时,表示找到解,记录并继续寻找其他解。 需要注意的是,每次回溯时需要恢复列、对角线等状态变量的值,确保下一次尝试的正确性。同时,非递归实现的效率可能略高于递归,因为减少了函数调用的开销。 现在,需要编写代码示例。根据引用中的C语言实现,可能使用数组来跟踪列、主对角线和次对角线的占用情况。每次放置皇后时标记这些位置,回溯时取消标记。栈中保存当前行和列的位置,以便回溯时继续搜索。 最后,验证算法是否正确,是否能够找到所有可能的解,并确保没有遗漏或重复的情况。此外,代码的可读性和效率也需要考虑,比如合理使用循环和条件判断来优化搜索过程。</think>### 非递归方式解决皇后问题算法实现 #### 核心思路 非递归算法通过**手动维护栈结构**模拟递归回溯过程,具体步骤为: 1. 用栈保存当前处理的行号`row`和列号`col`; 2. 用数组标记列、主对角线、次对角线是否被占用; 3. 通过循环逐行尝试放置皇后,冲突时回溯到上一行。 #### 算法步骤 1. **初始化**:栈中压入初始状态`(row=0, col=0)`; 2. **循环处理栈顶**: - 若当前行`row=8`,说明找到解,记录并回溯; - 否则在当前行中寻找可放置的列: - 若找到合法列,标记占用状态,压入下一行的初始列`(row+1, 0)`; - 若未找到,弹出栈顶回溯到上一行,恢复状态并尝试下一列。 --- #### C语言代码实现 ```c #include <stdio.h> #define N 8 int col[N] = {0}; // 列占用标记 int md[2*N-1] = {0}; // 主对角线标记(row - col + N-1) int sd[2*N-1] = {0}; // 次对角线标记(row + col) int stack[N][2]; // 模拟栈,保存(row, col) int top = -1; // 栈顶指针 void push(int row, int c) { top++; stack[top][0] = row; stack[top][1] = c; } void pop() { top--; } // 检查当前位置(row, c)是否合法 int is_valid(int row, int c) { return !col[c] && !md[row - c + N - 1] && !sd[row + c]; } void solve() { int row = 0, c = 0; push(row, c); while (top >= 0) { row = stack[top][0]; c = stack[top][1]; pop(); // 恢复状态(若之前已放置) if (row > 0) { int prev_c = stack[top][1]; col[prev_c] = md[row-1 - prev_c + N-1] = sd[row-1 + prev_c] = 0; } while (c < N) { if (is_valid(row, c)) { col[c] = md[row - c + N-1] = sd[row + c] = 1; push(row, c); if (row == N-1) { // 找到解 print_solution(); } else { // 处理下一行 push(row+1, 0); break; } } c++; } } } ``` --- #### 关键点说明 1. **状态恢复**:每次回溯时需要撤销当前行对列、对角线的占用标记[^3][^4]; 2. **栈操作**:通过`push`和`pop`模拟递归调用栈,保存搜索路径; 3. **时间复杂度**:与递归算法相同,均为$O(N!)$,但减少了函数调用开销[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值