C++ dfs状态的表示(五十三)【第十三篇】

今天我们将来求解N皇后问题。

1.N皇后问题

N 皇后问题是一个经典的问题,在一个 N×N 的棋盘上放置 N 个皇后,每行刚好放置一个并使其不能互相攻击(同一行、同一列、同一斜线上的皇后都会自动攻击)。

C++ dfs状态的表示(五十三)【第十三篇】_第1张图片

上图就是一个合法的 8 皇后的解。
N 皇后问题是指:计算一共有多少种合法的方法放置 N 个皇后。

很显然,我们依然会用 dfs 来求解 
N 皇后问题,我们的搜索策略如下。

从第 0 列开始,我们依次给每一列放置一个皇后,对于一个确定的列,我们只需要去枚举放置的行即可,在保证放置合法的情况下,一直搜索下去。

下图是 N=4 时搜索树的局部形态:

C++ dfs状态的表示(五十三)【第十三篇】_第2张图片

上图中每个状态下的 
−2,−1,0 表示的是该状态的冲突次数(在多少列或斜线发生冲突),只有当冲突次数为 
0 时才是合法状态。

确定了搜索策略,现在我们还有一个问题没有解决,那么就是如何判断是否发生了冲突。因为我们是逐列放置的,所以列内是不会冲突的,对于行也很好处理,如果某行被占用了,只需要用一个数组标记即可。

而最难处理的是同一斜线了。一种方法是我们可以直接暴力枚举这个皇后对应的两条对角线上的点,判断是否有冲突。但是这种方法,既麻烦,效率也比较低。下面我们介绍一种巧妙的方法。

C++ dfs状态的表示(五十三)【第十三篇】_第3张图片

有一个规律,这条对角线上的位置的行加列的值相同,都是 4。

0+4=1+3=2+2=3+1=4+0=4

并且每条对角线上行加列的值互不相同,所以我们就可以根据行加列的值判断是哪一条对角线。

C++ dfs状态的表示(五十三)【第十三篇】_第4张图片

而这样的对角线上的位置的坐标的行减去列的值也是相同的,都是 −2。

而我们发现,对于每条对角线,和值或者差值都是不一样的,而我们正好可以用这一点来标记一条对角线是否被占用,使得标记对角线就像标记列一样简单了。

但是可能存在负数,为了使负数变成整数,我们让差值再加上 
N,即:行 − 列 + N。

代码:

#include 
using namespace std;
int ans = 0;
bool row[10], x1[20], x2[20];
bool check (int i, int c) {
    return !row[i] && !x1[i+ c] && !x2[i - c + 8];//合法性
}
void dfs(int c) {//枚举第C列的摆放
    if (c == 8) {
        ans++;
        return ;
    }
    for (int i = 0; i < 8; i++) {//枚举第i列
        if (check(i, c)) {//无冲突就可以摆放
            row[i] = x1[i + c] = x2[i - c + 8] = true;
            dfs(c + 1);
            row[i] = x1[i + c] = x2[i - c + 8] = false;
        }
    }
}
int main() {
    dfs(0);
    cout << ans << endl;
    return 0;    
}

你可能感兴趣的:(C++,深度优先,算法)