题目描述
一个如下的 6 \times 66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:
行号 1\ 2\ 3\ 4\ 5\ 61 2 3 4 5 6
列号 2\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。 并把它们以上面的序列方法输出,解按字典顺序排列。 输出所有的解。最后一行是解的总个数。
输入格式
一行一个正整数 nn,表示棋盘是 n \times nn×n 大小的。
输出格式
输出所有解,每个解的两个数字之间用一个空格隔开。最后一行只有一个数字,表示解的总数。
输入数据 1
6
Copy
输出数据 1
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
5 3 1 6 4 2
4
Copy
提示
【数据范围】 对于 100\%100% 的数据,6 \le n \le 106≤n≤10。
题解
做这道题,我们要解决如下问题:
- 如何判断是否在 k 列 i 行可行?
- 放下一个皇后后,怎么让其所在行,列,两条斜线都不能再次放置另一个皇后?
- 如果某一列的每一行都无法再次放置皇后,要怎么回溯到上一次?
- 如果k==8时,输出一个可行解后怎么在查找下一个可行解?
- -最后我们要怎么退出?
A. 对于①、②问题,我们先要找到一种方式来储存可行解,当然我们也可以用二维数组来模拟棋盘,每放置一个皇后就把这位置标记下来,并把所在行列斜线都标记为false,再进行下一次放置皇后时,根据该行该列是否是 false 来放置。但自习思考这样会造成标记和输出上的麻烦 。为了使问题便于思考和简化代码,我们可以采取以下方法。
- 用一维数组 col[9] 来表示每列上皇放置的位置,如row[A]=B:代表第 A 列第9行放置了一个皇后。
- 用 bool row[9],digLeft[16],digRight [16]分别表示每行,左低右高线和右低左高线上是否被禁止放置皇后。如row[A]=false,digLeft[B]=false,digRight[C]=false,表示第 A 行,第B条左低斜线和第C条右高斜线上不能放置皇后。至于列就不用了管它了。通过规律我们可以把 B 和 C 用 k 和 i 表示出来。
B. 对于③,但循环 i=9 时自然会退出回溯到上一次放置皇后,不过我们要额外加一条代码来取消上一次的标记。对于④也是同理。
C. 当我们把所有能够试探的方法试探玩后,k=9,自然就会退出递归。
一切都思考清楚后就可以释放代码了,奥利给!
AC代码
#include <iostream>
using namespace std;
void queen_all(int k,int n);
int col[100]={0},times=0; //用times来记录一共多少种结果
bool row[100]={0},digLeft[100]={0},digRight[100]={0};
int main()
{
int n=0;
cin>>n;
queen_all(1,n);
cout<<times;
return 0;
}
void queen_all(int k,int n)
{
int i=0;
for (i=1;i<=n;++i)
{
if (row[i]!=1&&digLeft[k+i-1]!=1&&digRight[n+k-i]!=1)
{
col[k]=i;
row[i]=digLeft[k+i-1]=digRight[n+k-i]=1;
if (k==n)
{
for (int j=1;j<=n;++j)
cout<<col[j]<<' ';
cout<<endl;
++times;
}
else queen_all(k+1,n);
row[i]=digLeft[k+i-1]=digRight[n+k-i]=0;
}
}
}