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;
}
}
运行结果截图 :
欢迎大家提出改进意见,互相交流。
有疑问可留言。