题目描述
一个如下的 6 \times 66×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。
输入格式
一行一个正整数 nn,表示棋盘是 n×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入 #1复制
6
输出 #1复制
2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4
说明/提示
【数据范围】
对于 100% 的数据,6≤n≤13。
题目翻译来自NOCOW。
USACO Training Section 1.5
思路简介
因为对于每个皇后都不能是同行同列或者同对角线,显然每行都只能是放一个皇后。所以可以利用行号从1到n开始深搜。深搜的关键便是,解决好当前需要做好什么,至于下一步要做的,是和当前一样的,只不过是行号+1。细节详见代码
AC代码如下
#include<bits/stdc++.h>
using namespace std;
#define maxn 100
int a[maxn], n, ans = 0;
int b1[maxn], b2[maxn], b3[maxn];//b1用来记录某列是否能放皇后,b2和b3用来标记某对角线能否放
void dfs(int x) {//行号
if (x > n) {//如果行号大于n,说明前n行都排完了
ans++;//方法数++
if (ans <= 3) {//输出前三种方法
for (int i = 1; i <= n; i++)
cout << a[i] << " ";
cout << endl;
}
}
for (int i = 1; i <= n; i++) {//对于每一行都遍历每一列
if (b1[i] == 0 && b2[x + i] == 0 && b3[x - i + 15] == 0) {//x是行号,i是列号,同对角线即同直线,x+i和x-i都是定值 +15是为了防止x-i为负数,既然是定值,+上某值也是定值,所以+15无所谓大小,只是为了防止越界,+20等皆可。
a[x] = i;
b1[i] = 1;; b2[x + i] = 1; b3[x - i + 15] = 1;//标记此行此列以及此对角线
dfs(x + 1);//继续放下一列
b1[i] = 0;; b2[x + i] = 0; b3[x - i + 15] = 0;//取消标记
}
}
}
int main(){
cin >> n;
dfs(1);//从第一行开始放
cout << ans;
return 0;
}