检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。
//以下的话来自usaco官方,不代表洛谷观点
特别注意: 对于更大的N(棋盘大小N x N)你的程序应当改进得更有效。不要事先计算出所有解然后只输出(或是找到一个关于它的公式),这是作弊。如果你坚持作弊,那么你登陆USACO Training的帐号删除并且不能参加USACO的任何竞赛。我警告过你了!
输入格式:
一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。
输出格式:
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入样例#1:
6
输出样例#1:
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
题目翻译来自NOCOW。
USACO Training Section 1.5
题目链接:https://www.luogu.org/problem/show?pid=1219
分析:
显然问题的关键在于如何判定某个皇后所在的行,列,斜线上是否有别的皇后;可以从矩阵的特点上找到规律,如果在同一行,则行号相同;如果在同一列上,则列号相同;如果同在/ 斜线上的行列值之和相同;如果同在\ 斜线上的行列值之差相同
考虑每一行只能有一个皇后(根据题意同行不能有两个皇后)我们可以建立一个ans[]数组来储存每个皇后的位置ans[i]下表i代表行数内容代表列数
另外根据我们找到的规律可以建立a[],b[],c[],d[]四个数组分别用来标记对角线的四个方向,我们可以使用回溯算法放置皇后时对该皇后的行列对角线进行占用标记。一种方案执行完后,再释放标记!
此题采用的是一维数组操作,使问题变得更加容易理解!
下面给出AC代码:
1 #include <bits/stdc++.h>
2 using namespace std;
3 const int maxn=1000010;
4 typedef long long ll;
5 int a[maxn],b[maxn],c[maxn],d[maxn],ans[maxn],n,tim=0;
6 inline void print()
7 {
8 if(tim<3)
9 {
10 for(int i=1;i<n;i++)
11 cout<<ans[i]<<" ";
12 cout<<ans[n]<<endl;
13 }
14 ++tim;
15 return;
16 }
17 inline int DFS(int k)//深度优先搜索,k指代接下去放的是第k个棋子
18 {
19 if(k>n)
20 print();
21 else
22 {
23 for(int i=1;i<=n;i++)
24 {
25 if(a[i]==0&&b[i]==0&&c[i-k]==0&&d[i+k]==0)//判断行列对角线是否可以放
26 {
27 a[i]=1;
28 b[i]=1;
29 c[i-k]=1;
30 d[i+k]=1;
31 ans[k]=i;
32 DFS(k+1);//搜索下一个棋子的位置
33 a[i]=0;
34 b[i]=0;
35 c[i-k]=0;
36 d[i+k]=0;
37 }
38 continue;
39 }
40 }
41 }
42 int main()
43 {
44 scanf("%d",&n);
45 DFS(1);
46 printf("%d",tim);
47 return 0;
48 }