八皇后问题

问题描述

问题描述:在棋盘上放置 8 个皇后,使得它们互不攻击,此时每个皇后的攻击范围为同行同列同对角线,要求找出所有解。

LeetCode上面的描述:

20201217212430

解决方法

这里使用经典的回溯法。

代码:

下面的程序简洁地求解了八皇后问题。在主程序中读入 n,并为 tot 清零,然后调用 search(0),即可得到解的个数 tot。

void search(int cur) { // cur 代表当前行
    if (cur == n)
        tot++; // 递归边界, 只要走到了这里, 所有皇后必然不冲突, 因为这是从第 8 行(也就是最后一行)递归进来的
    else {
        for (int i = 0; i < n; i++) { // 对列进行遍历
            int ok = 1; // 记录当前的摆放位置是否会和之前已经放好的皇后发生冲突
            C[cur] = i;
            for (int j = 0; j < cur; j++) { // 检查是否和前面的皇后冲突
                if (C[cur] == C[j] || cur - C[cur] == j - C[j] || cur + C[cur] == j + C[j]) {
                    ok = 0;
                    break;
                }
            }
            if (ok)
                search(cur + 1);
        }
    }
}

这里的测试结果是 92,答案正确。

这个程序还可以改进,如下

int total = 0;
int vis[3][N * 2] = {0}; // vis[0] 表示列被占据的情况, vis[1] 表示主对角线(从左上到右下)被占据的情况, vis[2] 表示副对角线被占据的情况
void searchV2(int cur) {
    if (cur == n) {
        total++;
    } else {
        for (int i = 0; i < n; i++) {
            if (!vis[0][i] && !vis[1][cur + i] && !vis[2][cur - i + n]) { // 利用二维数组直接判断
                C[cur] = i;
                vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 1; // 修改全局变量
                searchV2(cur + 1);
                vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 0; // 切记! 一定要改回来
            }
        }
    }
}

关于 vis 数组的说明:它表示已经放置的皇后占据了那些列、主对角线和副对角线。上面的注释也详细说明了这一点。经测试,这个代码跑出来的结果是正确的。

完整代码

#include <iostream>
#include <ctime>

#define N 8

int n = 8;
int tot = 0; // total, 用于记录可能的种数
int C[N] = {-1, -1, -1, -1, -1, -1, -1, -1};

void search(int cur) { // cur 代表当前行
    if (cur == n)
        tot++; // 递归边界, 只要走到了这里, 所有皇后必然不冲突, 因为这是从第 8 行(也就是最后一行)递归进来的
    else {
        for (int i = 0; i < n; i++) { // 对列进行遍历
            int ok = 1; // 记录当前的摆放位置是否会和之前已经放好的皇后发生冲突
            C[cur] = i;
            for (int j = 0; j < cur; j++) { // 检查是否和前面的皇后冲突
                if (C[cur] == C[j] || cur - C[cur] == j - C[j] || cur + C[cur] == j + C[j]) {
                    ok = 0;
                    break;
                }
            }
            if (ok)
                search(cur + 1);
        }
    }
}


int total = 0;
int vis[3][N * 2] = {0}; // vis[0] 表示列被占据的情况, vis[1] 表示主对角线(从左上到右下)被占据的情况, vis[2] 表示副对角线被占据的情况
void searchV2(int cur) {
    if (cur == n) {
        total++;
    } else {
        for (int i = 0; i < n; i++) {
            if (!vis[0][i] && !vis[1][cur + i] && !vis[2][cur - i + n]) { // 利用二维数组直接判断
                C[cur] = i;
                vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 1; // 修改全局变量
                searchV2(cur + 1);
                vis[0][i] = vis[1][cur + i] = vis[2][cur - i + n] = 0; // 切记! 一定要改回来
            }
        }
    }
}

int main() {
    clock_t start_time, end_time;

    start_time = clock();
    for (int i = 0; i < 10000; i++) {
        tot = 0;
        search(0);
    }
    end_time = clock();
    std::cout<<end_time - start_time<<std::endl;
    std::cout<<"一共有"<<tot<<"种解法"<<std::endl;

    start_time = clock();
    for (int i = 0; i < 10000; i++) {
        total = 0;
        searchV2(0);
    }
    end_time = clock();
    std::cout<<end_time - start_time<<std::endl;
    std::cout<<"一共有"<<total<<"种解法"<<std::endl;
    return 0;
}

测试结果:

2897
一共有92种解法
1020
一共有92种解法
posted @ 2020-12-17 21:34  模糊计算士  阅读(139)  评论(0编辑  收藏  举报