问题描述:
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。
给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。
输入:
第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)
输出:
输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。
输入样例:
3
6
4
25
输出样例:
25713864
17582463
36824175
解决思路:
问题解决主要通过三步:
(1)生成八皇后串
(2)排序
(3)输入与输出
其中后两步较为简单,核心为如何生成八皇后串。
从第一行开始,选择可以使用的列(hashTable[i] == false),这种选择方法规避了在同行同列上不可能有两个皇后,所以递归时只用判断是否在同一对角线上。
递归边界:扫描的行数大于8(等于9)即停止递归,每一次在新的一行插入皇后时,都判断了该皇后是否与先前的皇后发生了冲突,若发生冲突将停止递归。所以,在达到边界条件时,一定是一个合法的八皇后串,可以直接将其存入结果数组中。
递归式(递归调用):对于第index行,首先找到一个没有插入过皇后的列(i),表示在第index行第i列插入皇后。首先判断其与先前插入的皇后是否冲突,若冲突结束递归,若不冲突则将其计入临时存储串的数组中。将hashTable(i)设为true,表示该列已经不能用了。再递归调用generate(index + 1),为下一行选择皇后。最后将hashTable(i)设为false还原该列的状态。
完整代码:
#include <stdio.h>
#include <algorithm>
using namespace std;
//1-7用于表示对应列是否有皇后
bool hashTable[10] = {false};
//用于存储八皇后串,每个串共八位,用int就足够
//num为res的下标
int res[92] = {0}, num = 0;
//用于临时存储现在的串
int temp[10] = {0};
void generate(int index)
{
//递归边界
if(index == 9)
{
//若能达到递归边界则一定是一个合法的方案
//将字符串转换为整数进行保存
for(int i = 1; i <= 8; i++)
{
res[num] = res[num] * 10 + temp[i];
}
num++;
return;
}
for(int i = 1; i <= 8; i++)//第i列
{
if(!hashTable[i])//该列没有皇后
{
bool flag = false;//flag用于标记该列是否冲突
//确保该列的皇后没有与之前的皇后发生冲突
//如果发生冲突就停止递归,这样保证了如果达到递归边界为合法方案
for(int j = 1; j < index; j++)
{
if(abs(index - j) == abs(temp[j] - i))
{//行列距离相等,表示在对角线上不满足条件
flag = true;
break;
}
}
if(!flag)
{//该列的皇后与之前的皇后没有发生冲突
temp[index] = i;
hashTable[i] = true;
generate(index + 1);
hashTable[i] = false;
}
}
}
}
int main()
{
//生成八皇后串
generate(1);
//排序
sort(res, res + num);
//输入与输出
int N;
scanf("%d", &N);
while(N--)
{
int i;
scanf("%d", &i);
printf("%d\n", res[i - 1]);
}
return 0;
}