【蓝桥杯】2n皇后问题Java解析(深度优先搜索+回溯)。

2n皇后问题题目描述 :

给定一个n*n的棋盘,棋盘中有一些位置不能放皇后。现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行、同一列或同一条对角线上,任意的两个白皇后都不在同一行、同一列或同一条对角线上 。问总共有多少种放法?n小于等于8。

输入格式 :

输入的第一行为一个整数n,表示棋盘的大小。接下来n行,每行n个0或1的整数,如果一个整数为1,表示对应的位置可以放皇后,如果一个整数为0,表示对应的位置不可以放皇后。

样例输入 :

4
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
输出 : 2

问题分析:在分析 2n皇后 问题前,我们先来看看此问题的另一个版本: n皇后问题

顾名思义,所谓的2n皇后问题,无非在n皇后的基础上增加了n个另一种颜色皇后。且两种颜色皇后互不影响。所以n皇后问题只要有n个同色皇后满足条件便可。

上图 :
在这里插入图片描述
现在我们可以设想:同样大小的棋盘,如果解得全部n皇后问题的解集,那么这些解集中只要有两种放法中皇后落子位置没有重复,那么就可以得到两种2n皇后的解法(颜色互换有两种放法)。
所以说,只要解决了n皇后问题,那么2n皇后问题便迎刃而解,且能去除多放n个皇后的运算和判断

N皇后问题

分析:
n*n规格的棋盘要放下n个皇后,那么说明每一行必须且仅能落下一个皇后。
这时候我们便能使用深度优先搜索算法去组合出所有的排列方式,并对每一步落子位置加以判断。
使其满足规则。
深度优先搜索算法 的本质便是是递归。递归函数部分代码如下:

	static public void dfs(int line,int[][] temp) {   //该次搜索行数,输入数据二维数组
		if(line<n) {                                  //递归函数出口
			for(int i=0;i<n;i++) {
				if(temp[line][i]==1&&check(line,i)) { //检查该位置是否能落子,并判断落子后是否满足规则
					flag[line][i] = 1;					//flag数组表示棋盘落子位置,1代表皇后
					dfs(line+1,temp);				 //进行下一行搜索
					flag[line][i]=0;					//回溯
				}
			}
		}else {
			StringBuilder sb = new StringBuilder();    //得到一种解法,将其位置信息以字符串形式保存在
			for(int i=0;i<n;i++) {					  //list集合中。
				for(int j=0;j<n;j++) {
					if(flag[i][j]==1)
						sb.append(""+j);
				}
			}
			list.add(sb.toString());
		}
	}

这样便能得到了所有的n皇后问题的解集,并将其位置信息保存在了ArrayList<string list集合中。(泛型<>里打的String为什么会消失?)
然后我们去两两对比解集中的元素,只要每一位字符都不相同。便能组合成两种2n皇后问题的解法。

比较函数代码如下:

	for(int i=0;i<list.size();i++) {
			for(int j=i+1;j<list.size();j++) {
				if(eqs(list.get(i),list.get(j)))
					num+=2;
			}
		}
	static public boolean eqs(String s1,String s2) {
		for(int i=0;i<s1.length();i++) {
			if(s1.charAt(i)==s2.charAt(i))
				return false;
		}
		return true;	
	}

解题完整代码:

import java.util.ArrayList;
import java.util.Scanner;

public class the2n皇后问题 {
	static int n;
	static int[][] flag; 
	static ArrayList<String> list = null;
	public static void main(String[] args) {
		Scanner s = new Scanner(System.in);
		n = s.nextInt();
		flag = new int[n][n];        //落子标记数组
		int[][] temp = new int[n][n];
		for(int i=0;i<n;i++) {
			for(int j=0;j<n;j++) {
				temp[i][j]= s.nextInt();
			}
		}
		list = new ArrayList<String>();
		dfs(0,temp);
		int num = 0;
		for(int i=0;i<list.size();i++) {
			for(int j=i+1;j<list.size();j++) {
				if(eqs(list.get(i),list.get(j)))
					num+=2;
			}
		}	
		System.out.println(num);
	}
	
	static public void dfs(int line,int[][] temp) {   //该次搜索行数,输入数据二维数组
		if(line<n) {                                  //递归函数出口
			for(int i=0;i<n;i++) {
				if(temp[line][i]==1&&check(line,i)) { //检查该位置是否能落子,并判断落子后是否满足规则
					flag[line][i] = 1;					//flag数组表示棋盘落子位置,1代表皇后
					dfs(line+1,temp);				 //进行下一行搜索
					flag[line][i]=0;					//回溯
				}
			}
		}else {
			StringBuilder sb = new StringBuilder();    //得到一种解法,将其位置信息以字符串形式保存在
			for(int i=0;i<n;i++) {					  //list集合中。
				for(int j=0;j<n;j++) {
					if(flag[i][j]==1)
						sb.append(""+j);
				}
			}
			list.add(sb.toString());
		}
	}
	
    public static boolean check(int line ,int row) {
    	int l = line; int r = row;
    	while(--l>=0&&--r>=0) {    //左上对角线检查
    		if(flag[l][r]==1)
    			return false;
    	}
    	   l = line; r = row;
    	while(--l>=0&&++r<n) {		//右上对角线检查
        	if(flag[l][r]==1)
        		return false;
       	}    
    	while(--line>=0) {
    		if(flag[line][row]==1)   //上方检查
    			return false;
    	}
		return true;
    }
    
	static public boolean eqs(String s1,String s2) {
		for(int i=0;i<s1.length();i++) {
			if(s1.charAt(i)==s2.charAt(i))
				return false;
		}
		return true;	
	}
}

运行结果截图 :
在这里插入图片描述
欢迎大家提出改进意见,互相交流。
有疑问可留言。

发布了2 篇原创文章 · 获赞 2 · 访问量 237

猜你喜欢

转载自blog.csdn.net/qq_42318488/article/details/105037937