原题:
八皇后
时间限制: 1 Sec 内存限制: 256 MB
题目描述
一个如下的6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列2 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:
行号1 2 3 4 5 6
列号2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。
输入格式
一行一个正整数 n,表示棋盘是 n×n 大小的。
输入格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入
6
输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
题意:
一道经典的DFS,题意不解释了题目说的很明白了。(逃
这道题在《一本通》里有解析和代码,这里简单讲一讲思路。
这是《一本通》中的源代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 bool d[10005],b[10005],c[10005]; 4 char a[14],e[14]={'A','A','B','C','D','E','F','G','H','I','J','K','L','M'}; 5 int sum,k; 6 int print() 7 { 8 int i; 9 sum++; 10 if(sum<=3) 11 { 12 for(i=1;i<=k;i++) printf("%d ",a[i]); 13 printf(" "); 14 } 15 } 16 int search(int i,int k) 17 { 18 int j; 19 for(j=1;j<=k;j++) 20 if(!b[j] && !c[i+j] && !d[i-j+k-1]) 21 { 22 a[i]=j; 23 b[j]=1; 24 c[i+j]=1; 25 d[i-j+k-1]=1; 26 if(i==k) print(); 27 else search(i+1,k); 28 b[j]=0; 29 c[i+j]=0; 30 d[i-j+k-1]=0; 31 } 32 } 33 int main() 34 { 35 scanf("%d",&k); 36 search(1,k); 37 printf("%d",sum); 38 return 0; 39 }
这道题首先很容易想到的是DFS暴力搜索,枚举所有点再考虑这个点是否可以安放棋子。有些第一次做这道题的萌新(比如之前的我)对于检验棋子是否可以安放无从下手,其实思路并不复杂。
仔细观察这张图的行号和列号。
你发现了什么?
找规律是个好办法
对角线的判断,我们可以对每一条假想的对角线编一个号,如按左下——右上方向编组,(1,1)属于一组,(1,2),(2,1)属于一组,(3,1),(2,2),(1,3)属于另一组......
很容易发现我们新编的组中,同组的每一对数中的行号和列号相加的值总为一个定值,所以我们用一个数组f2来记录我们定义的新组。
同样的按左上——右下编组,(1,6)属于一组,(1,5),(2,6)属于一组,(1,4),(2,5),(3,6)属于另一组......
在这个新编的组中,每一对数的行号和列号的差总为一定值(超简单对不对),所以我们用数组f3来标记这个新数组。
判断行、列是否重复很简单就不赘述了。
带解析的代码如下(换了个码风(^o^)假装是另一篇代码):
1 #include<iostream> 2 #include<stdio.h> 3 using namespace std; 4 int n,ans,pos[15],f1[25],f2[25],f3[25],f4[25]; 5 void print() 6 { 7 ans++; 8 if(ans>3) return;//只输出前三个答案 9 printf("%d",pos[1]); 10 for(int i=2;i<=n;i++) printf(" %d",pos[i]);//打印 11 printf(" "); 12 return; 13 } 14 void dfs(int x) 15 { 16 //x代表目前正在将第x颗棋子放置在第x行, 17 if(x==n+1) { print(); return; } 18 for(int i=1;i<=n;i++)//尝试第x行第i列 19 if(!f1[i] && !f2[i+x] && !f3[i-x+n]) 20 { 21 f1[i]=f2[i+x]=f3[i-x+n]=1,pos[x]=i; 22 //pos记录答案,f3数组中为防止下标为负数需要+n 23 dfs(x+1); 24 //回溯 25 f1[i]=f2[i+x]=f3[i-x+n]=0; 26 } 27 } 28 int main() 29 { 30 scanf("%d",&n); 31 dfs(1);//从第1枚棋子开始放置 32 printf("%d ",ans); 33 return 0; 34 }