点击查看代码
/*
n个皇后放置问题:
在一个n*n的国际象棋棋盘上放置n个皇后,使得这n个皇后两两均不在同一行、同一列、同一条对角线上,求合法的方案数
*/
#include<cstdio>
#include<cmath> //使用abs()
#pragma warning(disable:4996)
const int maxn = 11;
//P[i]=x表示第i列皇后的行号是x,HashTable[x]记录第x行是否已放置皇后,如果是则为true
int n, P[maxn], HashTable[maxn] = { false };
int count = 0; //统计合法方案数
/*
1、暴力法:
使用传统方法枚举所有情况,判断每种情况是否合法
处理当前排列的第index位,P[i]=x表示第i列皇后的行号是x
*/
void generateP(int index) {
//递归边界,排列的1~n位已经处理完,可以输出
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 == true) count++; //如果flag为true,则合法方案count加一
return; //结束递归返回上一层
}
//枚举1~n,将x存入P[index]中
for (int x = 1; x <= n; x++) {
//如果x还没存入P[0]~P[index-1]中,则本轮可以将x存入
if (HashTable[x] == false) {
P[index] = x; //将x存入P[index]中
HashTable[x] = true; //x已在P[]中
generateP(index + 1); //处理当前排列的第index+1位
HashTable[x] = false; //已经处理完P[index]为x的子问题,HashTable[x]要还原为false,留作下次使用
}
}
}
/*
2、回溯法:
当已经放置了一部分皇后时(生成了一个排列的一部分),如果此时剩余的皇后不管怎样放置都会与现有皇后在同一对角线上,
那么就不需要再继续向下递归,可以提前结束返回上一层,减少计算量
*/
void generateP(int index) {
//递归边界,生成一个合法方案
if (index == n + 1) {
count++;
return;
}
//第x行
for (int x = 1; x <= n; x++) {
//第x行还没有皇后
if (HashTable[x] == false) {
bool flag = true; //flag为true表示当前皇后与之前的皇后没有冲突
//将当前准备放置的皇后与之前的所有皇后比较,判断是否在同一对角线上
for (int pre = 1; pre < index; pre++) {
//第index列皇后的行号为x,第pre列皇后的行号为P[pre]
if (abs(index - pre) == abs(x - P[pre])) { //如果列号之差等于行号之差,则两个皇后在同一对角线
flag = false; //当前准备放置的皇后与之前的pre皇后在同一对角线上,该方案不合法,不用再继续向下递归
break; //结束循环
}
}
if (flag == true) { //当前准备放置的皇后与之前的皇后不冲突,则可以放置
P[index] = x; //第index列皇后的行号是x
HashTable[x] = true; //第x行已被占用
generateP(index + 1); //递归处理第index+1行皇后
HashTable[x] = false; //递归完成,还原第x行未被占用
}
}
}
}
int main() {
n = 8; //n=8(有8个皇后),合法方案数count=92
generateP(1); //从P[1]开始填充数字
printf("%d", count);
return 0;
}