题目描述
一个如下的 6×66 \times 66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2 4 6 1 3 52\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5 来描述,第 iii 个数字表示在第 iii 行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 61\ 2\ 3\ 4\ 5\ 61 2 3 4 5 6
列号 2 4 6 1 3 52\ 4\ 6\ 1\ 3\ 52 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 333 个解。最后一行是解的总个数。
输入格式
一行一个正整数 nnn,表示棋盘是 n×nn \times nn×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入 #1
6
输出 #1
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
说明/提示
【数据范围】
对于 100%100%100% 的数据,6≤n≤136 \le n \le 136≤n≤13。
笔者刚刚接触到dfs算法和回溯递归,关于dfs的思想仅有一点理解,处于大一的本人感觉与其说这道题是用dfs算法解答,倒不如说是回溯递归完美地实现了dfs算法.
对于n皇后问题,按行选择一个位置放置,对于任何一个被放置的点,假设其坐标为(x,y),其中x为行y为列,自左上到右下与其处于同一斜线的点其坐标之差为定值且等于x-y,自左下到右上与其处于同一斜线的点其坐标之和为定值且等于x+y。基于这一点分别建立三个数组(同列的y坐标相同)即可对放置皇后的坐标的两条斜对角线和同列直线进行标记(因为是逐行放置一个皇后不存在同行的情况),为了防止出现负数(数组的下标只能为正或0),用x-y+n(n为皇后数)来代替x-y即可。
#include <bits/stdc++.h>
#include <iostream>
using namespace std;
int n, a[3][100] = {0}, sum = 0, lay[100];//n为皇后数,sum存储解的个数,lay为放置皇后的点(输出)
//用行数为3的二维数组即可存储一个点的所处的三条直线的**坐标特征**,当然用3个一维数组也可
void dfs (int l)
{
if (l > n)
{
sum ++;
if (sum > 3) return;
for (int i = 1; i <= n; i++)
cout << lay[i] << " ";
cout << endl;
return;
}
for (int i = 1; i <= n; i++) {
if ((!a[0][i]) && (!a[1][l + i]) && (!a[2][l - i + n]))
{
a[0][i] = 1;
a[1][l + i] = 1;
a[2][l - i + n] = 1;
lay[l] = i;
dfs (l + 1);
a[0][i] = 0;
a[1][l + i] = 0;
a[2][l - i + n] = 0;
}
}
}
int main()
{
cin >> n;
dfs (1);
cout << sum << endl;
return 0;
}
关于回溯递归的关键,在于递归的退出条件和递归前递归后的语句.