思路:
从棋盘的第一列开始,尝试在当前列的每一行摆放皇后。如果当前在行列上摆放暂时不会引起冲突,则保存摆放的位置。然后进入下一行重复上述操作,直到每一列都完成操作。
解答:
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
/*
从棋盘的第一列开始,尝试在当前列的每一行摆放皇后。
如果当前在行列上摆放暂时不会引起冲突,则保存摆放的位置。
然后进入下一行重复上述操作,直到每一列都完成操作。
*/
int plan_num = 0; // 方案数量
int row_of_colm[11]; // 0元素弃置,第i个元素表示第i列上的皇后所在行的序号
bool if_occupied[11] = {false}; // 0元素弃置,第1到10个元素分别表示第1到10行时候已经放了皇后
int plan[92]; // 数组的每个元素表示一种方案
void NQueneP(int colm, int n) { // colmumn表示列号
if(colm == n + 1) { // 递归边界
plan_num++;
for(int i = 1; i <= n; i++) { // 摆放方案保存为一个整数
plan[plan_num] += row_of_colm[i] * pow(10, i - 1);
}
return; //返回上一层递归出口
}
//从第一行开始枚举试图摆放皇后
for(int row = 1; row <= n; row++) {
if(if_occupied[row] == false) { //当前行未被占用
bool flag = true; //试图在colm列row行摆放皇后
for(int pre = 1; pre < colm; pre++) { // 此时第1列到第colm-1列已处理完,这些列上的皇后已摆好,第i列上的皇后所在行号保存在row_of_colm[i]中,遍历前面的列检查是否会与当前行列上的皇后有对角冲突
if(abs(colm - pre) == abs(row - row_of_colm[pre])) { // 如果前面某皇后的行列与当前行列的差的绝对值相当,则对角冲突
flag = false; //不能在当前行列摆放
break;
}
}
//若为false,则不能在当前行列摆放,基于进入本次递归前确定的摆放位置加上本次探查的位置组成的摆放方案已经不可能成功,无需进一步摆放下去,直接进入下一行
if(flag == true) {
row_of_colm[colm] = row; //在colm列row行摆放皇后
if_occupied[row] = true; //标记row行已被占用
NQueneP(colm+1, n); //进行下一列处理
// 到达此处表明基于该行列的情况已经探查完,循环准备进入下一行
if_occupied[row] = false; // 清除占用标记
}
}
}
}
int main(){
int n = 8; // 皇后数
NQueneP(1, n); //从第一列开始摆皇后
sort(plan + 1, plan + (plan_num + 1));
int test_num;
while(scanf("%d", &test_num) != EOF) {
for(int i = 1; i <= test_num; i++) {
int index;
scanf("%d", &index);
printf("%d\n", plan[index]);
}
}
/*
cout << "共有" << plan_num << "种摆法" << endl;
for(int i = 0; i <= 92; i++) {
printf("%d %d\n", i, plan[i]);
}
*/
return 0;
}
笔记:
- 代码是比照《算法笔记》中对应部分写的,原书中的代码的变量名让人有点难以理解代码的逻辑,实际上就是遍历每一列,在每一列上遍历每一行。
- 我个人认为书中提到这段代码的回溯思想,应该是体现在某个位置上发现会与之前摆好的皇后起冲突后,就跳过进一步的摆法,直接探查当前列下一行。但这好像与书中说的“五皇后时摆好351后可以发现剩下两个位置一定会冲突”不太一样,实际上我都不太明白它是怎样发现一定会冲突。
- codeup编译错误可以点击查看提示。