Problem Description
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
考虑每行只能放一个皇后,每列也只能放一个皇后,那么如果把N列皇后所在的行号依次写出,那么就会是1~n的一个排列。
因此就只需要枚举1~n的所有排列,查看每个排列的放置方案是否合法,统计其中合法的方案即可。
总共有n!个排列。
于是可以在全排列的代码基础上进行求解,由于当到达递归边界时表示生成了一个排列,所以需要在其内部判断是否为合法方案,即遍历两个皇后,判断它们是否在同一条对角线上(不在同一行和同一列是显然的),若不是,则累计计数变量count即可。代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 11;
//P为当前排列, hashTable记录整数x是否已经在p中
int n, P[maxn], hashTable[maxn] = {false};
int cnt = 0;
void generateP(int index){
if(index == n + 1){ //递归边界,生成一个排列
bool flag = true; //flag为true,表示当前排列为一个合法方案
/*
在二维数组中,处在同一对角线上的两个元素
它们的 |行下标相减| == |列下标相减|
*/
for(int i = 1; i <= n; i++){ //遍历任意两个皇后
for(int j = i + 1; j <= n; j++){
// 行相减 列相减
if(abs(i - j) == abs(P[i] - P[j])){ //如果在同一条对角线上
flag = false;
}
}
}
if(flag)cnt++; //方案合法 +1
return;
}
//我们从下标为1的位置 开始填 这样对应的位置更加直观一些
for(int x = 1; x <= n; x++){
if(!hashTable[x]){
P[index] = x;
hashTable[x] = true;
generateP(index + 1);
hashTable[x] = false;
}
}
}
int main(){
n = 8;
generateP(1);
cout << cnt << endl;
return 0;
}
下面是回溯法
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 11;
//P为当前排列, hashTable记录整数x是否已经在p中
int n, P[maxn], hashTable[maxn] = {false};
int cnt = 0;
void generateP(int index){
if(index == n + 1){ //递归边界,生成一个合法方案
cnt++; //能达到这里的一定是合法的
return;
}
for(int x = 1; x <= n; x++){ //第x行
if(!hashTable[x]){ //第x行没有皇后
bool flag = true; //为true时 表示当前皇后不会和之前的皇后冲突
for(int pre = 1; pre < index; pre++){
//第index列皇后的行号为x,第pre列皇后的行号为P[pre]
if(abs(index - pre) == abs(x - P[pre])){
flag = false; //与之前的皇后在一条对角线上,冲突
break;
}
}
if(flag){ //如果可以吧皇后放在第x行
P[index] = x; //令第index列皇后的行号为x
hashTable[x] = true; //第x行已经被占用
generateP(index + 1); //递归处理第 index + 1行皇后
hashTable[x] = false; //递归完毕,还原第x行为 未占用
}
}
}
}
int main(){
n = 8;
generateP(1);
cout << cnt << endl;
return 0;
}