题目描述
一个如下的 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 2 4 6 1 3 5来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下:
行号 1 2 3 4 5 6
列号 2 4 6 1 3 5
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 33 个解。最后一行是解的总个数。
输入格式
一行一个正整数 n,表示棋盘是 n×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入
6
输出
2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4
说明/提示
【数据范围】
对于 100%100% 的数据,6≤n≤13。题目翻译来自NOCOW。
USACO Training Section 1.5
思路
本题就是八皇后问题的变形,关键在于不能同行、同列、同对角线(尤其是对角线),理清判断条件以后再用DFS。
关于对角线,可以找到规律,一共两类对角线,一类是左上到右下(dj1),一类是右上到左下(dj2),每一类对角线都有2n-1条,因此可以创建两个大小为2n-1的数组,设有元素num[i][j],对于dj1上的点,有j-i为递增的定值,对于dj2上的点,有j+i为递增的定值,找到规律后添加判定条件,具体代码如下。
代码
import java.util.*;
public class DFS_QUEEN {
public static int count;
public static int n;
public static int[][] num;
public static boolean[] hang;//不能同行
public static boolean[] lie;//不能同列
public static boolean[] dj1;//左上到右下的对角线
public static boolean[] dj2;//右上到左下的对角线
//初始化num数组
public static void init() {
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
num[i][j] = 0;
}
}
}
//输出
public static void printqueen() {
for(int i=0;i<n;i++) {
for(int j=0;j<n;j++) {
if(num[i][j]==1)
System.out.print(j+1+" ");
}
}
System.out.print("\n");
}
//x是行数
public static void dfs(int x) {
if(x==n) {
count++;
//只输出前三个解
if(count<=3)
{
printqueen();
}
return;
}
for(int i=0;i<n;i++) {
if(!dj2[i+x]&&!hang[x]&&!lie[i]&&!dj1[i-x+n-1]) {
num[x][i]=1;
hang[x]=true;
lie[i]=true;
dj1[i-x+n-1]=true;//加n-1防止下标越界
dj2[i+x]=true;
dfs(x+1);
//遍历完后需要重置
num[x][i]=0;
hang[x]=false;
lie[i]=false;
dj1[i-x+n-1]=false;
dj2[i+x]=false;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner scan=new Scanner(System.in);
n=scan.nextInt();
scan.close();
num=new int[n][n];
hang=new boolean[n];
lie=new boolean[n];
dj1=new boolean[2*n-1];
dj2=new boolean[2*n-1];
init();
dfs(0);//从第0层开始
System.out.print(count);
}
}