n-皇后问题是回溯法中经典中的经典,其基本问题描述是:在一个nxn的格子中放n个皇后,使得每个皇后不能相互攻击,任意两个皇后能够互相攻击的条件是他们在同一条对角线或者同一行或者同一列上
问题可以转换为从第0行开始放置皇后一直放到n-1行,使得每一行在放置皇后的同时,不能与前面的皇后相互攻击
即列,对角线不冲突即可(行肯定不冲突)
伪代码描述
search(cur) { //表示开始放置第cur个皇后,当然行数为cur,放置皇后的时候主要是确定cur的列
1.如果所有皇后都放置完毕,则打印出来
2.尝试将第cur个皇后放在第j列(0<=j<=n-1),如果放在某一列j使得该位置与前面cur-1个皇后不冲突,那么这个位置是合理的
可以将其放在该位置,并且在这种放置方案下继续放置第cur+1个皇后
}
c++代码描述
#include <iostream>
using namespace std;
void search(int cur, int c[], int n) {
if (cur == n) {
for (int i = 0; i < n; i++){
cout << " ";
for (int j = 0; j < n; j++)
if (j == c[i])
cout << "Q ";
else
cout << "* ";
cout << endl;
}
cout << endl;
} else {
for (int j = 0; j < n; j++) {
//先检查是否和前面cur-1个皇后有冲突
int ok = 1;
c[cur] = j; //尝试把cur放到第j列
//检查(cur, c[cur]) 与(i, c[i])是否不能攻击
for (int i = 0; i < cur; i++) {
if (c[cur] == c[i] || cur - c[cur] == i - c[i] || cur + c[cur] == i + c[i]) {
ok = 0;
break;
}
}
if (ok) {
search(cur+1, c, n);
}
}
}
}
int main() {
int c[4];
search(0, c, 8);
return 0;
}
程序的效率还可以提高,主要是将一个nxn的方格的对角线进行编号,首先,我们应该注意到,放置任意一个皇后之后,该皇后所在的列,主对角线,副对角线都不能放置任何皇后了,上面的算法是用遍历来查询正确的位置,而这里,我们每放置一个皇后之后,可以给对应的主对角线,副对角线加上标志,关于主对角线,副对角线,可以用下面一幅图来说明
所以,我们可以用一个3x(2n)的数组来标志对应的列,主对角线,副对角线上是否已经有皇后
vis[0][index] = 1标示第index列上已经有皇后
vis[1][index] = 1标示第index条主对角线上已经有皇后
vis[2][index] = 1标示第index条副对角线上已经有了皇后
那么我们简单推理一下格子(x, y)使得
vis[0][y] = 1
vis[1][y-x+n] = 1 //为防止出现负数下标,所以每一个对应的编号加上n
vis[1][y+x] = 1
最后,我们用c++代码来实现
#include <iostream>
using namespace std;
int n = 4;
int v[2][8];
int c[4];
void search(int cur) {
if (cur == n) {
for (int i = 0; i < n; i++){
cout << " ";
for (int j = 0; j < n; j++)
if (j == c[i])
cout << "Q ";
else
cout << "* ";
cout << endl;
}
cout << endl;
} else {
for (int j = 0; j < n; j++) {
if (!v[0][j] && !v[1][j-cur+n] && !v[2][cur+j]) {
c[cur] = j;
v[0][j] = v[1][j-cur+n] = v[2][cur+j] = 1;
search(cur+1);
v[0][j] = v[1][j-cur+n] = v[2][cur+j] = 0;
}
}
}
}
int main() {
search(0);
return 0;
}