八皇后(洛谷) JAVA

题目描述

一个如下的 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2 4 6 1 3 5 来描述,第 ii 个数字表示在第 ii 行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6

列号 2 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。

输入格式

一行一个正整数 n,表示棋盘是 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。

题目描述:由于题目说明了,每行每列有且仅有一个棋子,每条对角线至多有一个棋子,意思就是每个棋子占领的位置其八个方向所在直线上不能再有其他棋子。

例如:(2,4)点有个棋子

 如上图所示,由于(2,4)被一个棋子占领,那么它所在的行,列,两个对角线都不能再有其他棋子了(用×代表不能再放入棋子)。

 由上述分析我们可以得出一个n*n型的棋盘要想达到题目要求得有n个棋子,这就是退出条件。

解题思路:由上述分析,我们应该大致能想到用深度优先搜索,和回溯来完成本题,但难处在于,若我们用二维素组来标记每个点是否被访问过,在标记的时候我们发现并没有什么问题,但是在回溯的时候我们应该如何控制回溯的区间呢,这就是本题的一个核心之处。若我们利用二维数组来访问,那么回溯的时候会相当麻烦,可能将上一个点的访问区间也回溯了,这会导致我们答案的个数会变多。既然棋子所在行,列,对角线不能有其他棋子,那么行,列,对角线分别用一维数组来表示会怎样?这就是解题的关键所在。由于行,列,对角线是由独立一维数组表示,那么在回溯的时候就不会影响到上一个棋子的标记。问题也就因此迎刃而解,也就少走不少弯路。

理论成立代码如下:

import java.util.*; 
public class Main {
    public static void main(String args[]) {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int num=n+1;
    solution s = new solution(n);
    for(int j = 1;j < num;j++)//从第一行为起始遍历点
    {
      s.b[1] = 1;
      s.c[j] = 1;
      s.d[1 + j] = 1;
      s.e[1 - j + num] = 1;//标记此位置已被占领。
      s.dfs(1, j, 1);//k由1开始储存
      s.b[1] = 0;
      s.c[j] = 0;
      s.d[1 + j] = 0;
      s.e[1 - j + num] = 0;//回溯
    }
    System.out.print(s.flag);
    sc.close();
    }
}
class solution
{
  int N;//边界
  int a[];//储存路径
  int b[];//标记横轴
  int c[];//标记纵轴
  int d[];//标记左下角到右上角的对角线。
  int e[];//标记左上角到右下角的对角线。
  int flag;//记录方法数
  solution(int n)
  {
	 N = n+1;//由于整个棋盘由1开始所以+1。
	 a = new int[100];//100个空间完全够用
	 b = new int[100];
	 c = new int[100];
	 d = new int[100];
	 e = new int[100];
  }
  public void dfs(int xx, int yy, int k)//深度遍历+回溯
  {
	  a[k] = yy;//储存纵坐标。
	  if(k == N - 1)//结束条件
	  {
	   print(k);
	   return ;
	  }
	  for(int j = 1;j < N;j++)//开始遍历下一行
	  {
		int fx=xx+1;
		if(fx >= 2&&fx < N&&b[fx] != 1&&c[j] != 1&&d[fx+j] != 1&&e[fx-j+N]!=1)//判断是否符合要求
		{
		   b[fx] = 1;
		   c[j] = 1;
		   d[fx + j] = 1;
		   e[fx - j + N] = 1;
		   //占领
		   dfs(fx, j, k + 1);//储存此点,并探索下一行。
		   b[fx] = 0;
		   c[j] = 0;
		   d[fx + j] = 0;
		   e[fx - j + N] = 0;//回溯
		}
	  }
  }
  public void print(int k)
  { 
	if(flag <= 2)
	{
	  for(int i = 1;i < k;i++)
	  System.out.print(a[i]+" ");
	  System.out.println(a[k]);
	}
    flag++;
  }
}

 由此题我也是收获良多,在标记地图是否被访问时,可以有多种方式,我们不应该过于局限于某种,同时要想用代码去实现一道题,首先本人得先明白题意,会去做这道题,这是写对代码的前提。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值