[递归回溯] 八皇后问题

例题(15.4) 八皇后 (1060)

题目描述

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。
给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小

关于输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)

关于输出

n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串

例子输入
2
1
92
例子输出
15863724
84136275
解题分析

我们可以使用深度优先搜索(DFS)和回溯的方法来解决八皇后问题。以下是算法的详细步骤:

1. 初始化棋盘:首先,我们创建一个一维数组`board`来代表棋盘,数组的索引代表棋盘的行,索引对应的值代表在该行的皇后所在的列。例如,`board[0] = 3`表示第一行的皇后在第四列。

2. 搜索所有可能的解:我们使用递归函数`solve`来进行深度优先搜索。在这个函数中,我们从第一行开始,尝试在每一列放置皇后。对于每一列,我们使用`is_valid`函数来检查在这个位置放置皇后是否有效。如果有效,我们就在这个位置放置皇后,然后递归地在下一行做同样的事情。如果我们已经在所有的行上放置了皇后,那么我们就找到了一个解,将其添加到`solutions`数组中。

3. 检查有效性:在`is_valid`函数中,我们检查新的皇后是否和已经放置的皇后在同一列或同一对角线上。我们通过比较新的皇后的位置和已经放置的皇后的位置来实现这一点。如果新的皇后和任何一个已经放置的皇后在同一列或同一对角线上,那么这个位置就是无效的,我们需要在其他列上尝试放置皇后。

4. 处理输入和输出:在`main`函数中,我们首先调用`solve`函数来找到所有的解,然后处理输入数据。对于每个输入的索引,我们从`solutions`数组中找到相应的解,然后打印出来。

这个算法的时间复杂度是O(N!),其中N是棋盘的大小(在这个问题中,N=8)。这是因为在最坏的情况下,我们需要尝试所有可能的皇后放置方式。然而,由于我们使用了回溯的方法,当我们发现某个皇后的位置无效时,我们会立即停止在这个路径上的搜索,这大大减少了搜索空间,提高了效率。

并且注意到:

在国际象棋中,两个棋子处在同一对角线上,当且仅当它们的行索引之差等于列索引之差,或者行索引之差等于列索引之差的负数。这是因为在对角线上的每个方格,其行和列的增减速度是相同的。

在我们的代码中,我们使用了以下的代码来检查新的皇后是否和已经放置的皇后在同一对角线上:

```c
abs(board[i] - col) == abs(i - row)
```

这里,`board[i]`和`i`分别是已经放置的皇后的列和行,`col`和`row`是新的皇后的列和行。如果新的皇后和已经放置的皇后在同一对角线上,那么他们的行索引之差`abs(i - row)`应该等于列索引之差`abs(board[i] - col)`。

这样,我们就可以很容易地检查新的皇后是否和已经放置的皇后在同一对角线上。

代码实现
#include 
#include 

#define N 8
#define TOTAL_SOLUTIONS 92

int solutions[TOTAL_SOLUTIONS][N] = {0};
int count = 0;

int is_valid(int board[N], int row, int col) {
    for (int i = 0; i < row; i++) {
        if (board[i] == col || abs(board[i] - col) == abs(i - row)) {
            return 0;
        }
    }
    return 1;
}

void solve(int board[N], int row) {
    if (row == N) {
        for (int i = 0; i < N; i++) {
            solutions[count][i] = board[i] + 1;
        }
        count++;
    } else {
        for (int col = 0; col < N; col++) {
            if (is_valid(board, row, col)) {
                board[row] = col;
                solve(board, row + 1);
            }
        }
    }
}

int main() {
    int board[N] = {0};
    solve(board, 0);

    int t;
    scanf("%d", &t);
    while (t--) {
        int b;
        scanf("%d", &b);
        for (int i = 0; i < N; i++) {
            printf("%d", solutions[b-1][i]);
        }
        printf("\n");
    }
    return 0;
}

当然,我们还可以选择另外一种办法:

这个程序的目标和之前的程序一样,都是要解决八皇后问题。它使用了一种稍微不同的方法来实现深度优先搜索和回溯。

以下是这个程序的主要步骤:

1. 初始化棋盘和其他相关数组:`board`数组用来存储每个皇后的位置,`left1`和`right1`数组用来存储左对角线和右对角线上已经放置了皇后的位置,`col`数组用来存储已经放置了皇后的列。`solution`数组用来存储所有的解,`count`变量用来记录已经找到的解的数量。

2. 搜索所有可能的解:`dfs`函数是一个递归函数,用于通过深度优先搜索来找到所有可能的解。在这个函数中,我们从第一行开始,尝试在每一列放置皇后。对于每一列,我们检查在这个位置放置皇后是否有效,即这个位置的列、左对角线和右对角线上是否已经放置了皇后。如果有效,我们就在这个位置放置皇后,并标记这个位置的列、左对角线和右对角线已经被占用,然后递归地在下一行做同样的事情。如果我们已经在所有的行上放置了皇后,那么我们就找到了一个解,将其添加到`solution`数组中。

3. 处理输入和输出:在`main`函数中,我们首先调用`dfs`函数来找到所有的解,然后处理输入数据。对于每个输入的索引,我们从`solution`数组中找到相应的解,然后打印出来。

这个程序的关键在于它如何使用`col`、`left1`和`right1`数组来快速检查在特定位置放置皇后是否有效。这是一种常见的方法,用于优化八皇后问题的解决方案。通过这种方法,我们可以在常数时间内检查在特定位置放置皇后是否有效,这大大提高了算法的效率。

在这个程序中,`left1`和`right1`数组被用来检查在特定位置放置皇后是否会与已经放置的皇后在同一对角线上。这是通过计算行和列的索引之和或之差来实现的。

首先,我们需要理解在一个二维的棋盘上,对于任何一个在对角线上的格子,它们的行索引和列索引之和(对于右对角线)或之差(对于左对角线)都是常数。

例如,对于右对角线,如果我们从左上角开始,向右下角移动,那么每个格子的行索引和列索引之和都是相同的。同样,对于左对角线,如果我们从左下角开始,向右上角移动,那么每个格子的行索引和列索引之差都是相同的。

在这个程序中,`right1[row+j]`被用来表示右对角线,`row+j`的值对于同一对角线上的所有格子都是相同的。`left1[row-j+9]`被用来表示左对角线,`row-j+9`的值对于同一对角线上的所有格子都是相同的。这里加9是为了避免数组索引为负数。

如果在某个位置放置了皇后,那么我们就将相应的`left1`和`right1`数组的值设为1,表示这个对角线上已经放置了皇后。在尝试在其他位置放置皇后时,我们就可以通过检查`left1`和`right1`数组来快速确定这个位置的对角线上是否已经放置了皇后。

因此,`left1`和`right1`数组是一种有效的方法,可以在常数时间内检查在特定位置放置皇后是否会与已经放置的皇后在同一对角线上。

#include 
using namespace std;

int board[9]={0},left1[20]={0},right1[20]={0},col[9]={0};
int solution[93][9];
int count=1;

void dfs(int row,int step){
    if(step>8){
        for(int i=1;i<=8;i++){
            solution[count][i]=board[i];
        }
        count++;
        return;
    }
    for(int j=1;j<=8;j++){
        if(col[j]==0 && left1[row-j+9]==0 && right1[row+j]==0){
            board[row]=j;
            col[j]=1; left1[row-j+9]=1; right1[row+j]=1;
            dfs(row+1,step+1);
            col[j]=0; left1[row-j+9]=0; right1[row+j]=0;
        }
    }
}

int main() {
    int n; cin>>n;
    dfs(1,1);
    while(n--){
        int b; cin>>b;
        for(int i=1;i<=8;i++){
            cout<

你可能感兴趣的:(算法,数据结构,leetcode)