一、算法要求
在nxn格的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于,在nxn格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线上。
1. 思路
在第i行第j列放置一个皇后,那么第i行的其他位置(同行),那么第j列的其他位置(同列),同一斜线上的其他位置,都不能再放置皇后。
可以以行为主导:
·在第1行第1列放置第1个皇后。
·在第2行放置第2个皇后。第2个皇后的位置不能和第1个皇后同列、同斜线,不用再判断是否同行了,因为每行只放置一个,本来就已经不同行。
·在第3行放置第3个皇后,第3个皇后的位置不能和前2个皇后同列、同斜线。
……
·在第t行放置第t个皇后,第t个皇后的位置不能和前t-1个皇后同列、同斜线。
……
·在第n行放置第n个皇后,第n个皇后的位置不能和前n-1个皇后同列、同斜线。
2. 示例
二、完整代码
1. 主文件
main.cpp:
// Project2: n后问题
#include<iostream>
#include<iomanip>
#include<cmath>
using namespace std;
const int numMax = 1000;
int n, //n个皇后
countn = 0, //可行解的个数
x[numMax]; //放置在第i行第x[i]列
bool Place(int t) { //判断第t个皇后能否放置在第i个位置
bool opt = true;
for (int j = 1; j < t; j++) {//判断是否与已放置的皇后冲突
if (x[t] == x[j] || fabs(t - j) == fabs(x[t] - x[j])) {
opt = false;
break;
}
}
return opt;
}
void Backtrack(int t) {
if (t > n) {//成功
countn++;
//打印结果
cout <<"#No." << countn << ": ";
for (int i = 1; i <= n; i++) {
cout << setw(3) << x[i];
}
cout << endl;
}
else
for (int i = 1; i <= n; i++){//分别判断n个分支
x[t] = i;
if (Place(t))
Backtrack(t + 1);
}
}
int main() {
cout << "#Please enter the number of queens(n): ";
cin >> n;
cout << "\n#The conditions are as follows: " << endl;
Backtrack(1);
cout << "\n#The number of answers is: "
<< countn << endl;
return 0;
}
2. 效果展示
三、补充
算法复杂度分析:
(1)时间复杂度
n皇后问题的解空间是一棵m (m=n)叉树,树的深度为n。最坏情况下,除了最后一层外,有1+n+n2 +…+nn-1=(nn-1)(n-1)~nn-1个结点需要扩展,而这些结点每个都要扩展n个分支,总的分支个数为nn,每个分支都判断约束函数,判断约束条件需要O(n)的时间,因此耗时O(nn+1)。在叶子结点处输出当前最优解需要耗时O(n),在最坏情况下回搜索到每一个叶子结点,叶子个数为nn,故耗时为O(nn+1)。因此,时间复杂度为O(nn+1)。
(2)空间复杂度
回溯法的另一个重要特性就是在搜索执行的同时产生解空间。在所搜过程中的任何时刻,仅保留从开始结点到当前扩展结点的路径,从开始结点起最长的路径为n。程序中我们使用x[]数组记录该最长路径作为可行解,所以该算法的空间复杂度为O(n)。
文档供本人学习笔记使用,仅供参考。