八皇后问题和2n皇后问题
无论是八皇后问题还是2n皇后问题,都用到了递归算法和回溯算法,下面,将对这两种问题进行分别的介绍。
一、八皇后问题
国际象棋中的皇后比中国象棋里的大车还厉害,皇后能横向,纵向和斜向移动,在这三条线上的其他棋子都可以被吃掉。所谓八皇后问题就是:将八位皇后放在一张8x8的棋盘上,使得每位皇后都无法吃掉别的皇后,(即任意两个皇后都不在同一条横线,竖线和斜线上),问一共有多少种摆法。
思路分析:
1、为了代码便于阅读,我们根据题目分析,可以判断出该题目中需要写两个方法:
- 判断该位置是否合法,即是否可以放置皇后:iscanput();
- 开始放置皇后:putQueen();
2、首先,拿着一个皇后在第一行进行放置,一旦找到合法的位置,即进入下一行(递归算法),对皇后再进行放置;
3、如果该行中每一个位置都不合法,即都不能满足放置皇后的条件,我们便采取回溯算法,说明上一层的皇后放置的位置不合适,调整上一行的皇后的放置位置;如果还是不合法,继续向上一层回溯…
下面我们将代码分开介绍:(其中关键代码已加注释)
1、判断该位置是否可以放置皇后:ifcanput()
代码如下:
//判断该位置是否可以放置皇后
public static boolean ifcanput(int row,int col,int[][] arr) {
// 一行是否有皇后
for(int i =0;i<arr.length;i++) { //此处的i表示列,所以i从0开始;
if(arr[row][i] == 0) {
return false;
}
}
// 一列是否有皇后
for(int i = 0;i<arr.length;i++) { //此处的i表示行,所以i从0开始;
if(arr[i][col] == 0) {
return false;
}
}
// 对角线是否有皇后(对角线分为四部分分析)
for(int i = 1;i<arr.length;i++) { //此处的i表示距离现在的col和row的距离是多少,所以i从1开始
//右下
if(col + i <arr.length &&row + i < arr.length) {
if(arr[row+i][col+i] == 0) {
return false;
}
}
// 左上
if(col - i >= 0 && row - i >= 0) {
if(arr[row - i][col-i] == 0) {
return false;
}
}
//左下
if(row + i < arr.length && col - i >= 0) {
if (arr[row+i][col-i] == 0) {
return false;
}
}
//左上
if (row - i >= 0 && col + i < arr.length) {
if (arr[row-i][col+i] == 0) {
return false;
}
}
}
return true;
}
2、开始放置皇后方法:putQueen()
代码如下:
//放置皇后
static int res = 0; //全局变量
public static void putQueen(int row,int col , int[][] arr) {
if(row == arr.length) { //行数等于棋盘长度,说明已经到最后一行
res ++; //记录下一次结果
return; //结束函数,不再回调
}
//判断一行的每一列是否都可以放置皇后
for(int i = 0;i<arr.length;i++) {
if(arr[row][i] != 1 ) { //如果该处不为1,则说明该位置已经放置了皇后;
continue;
}
if (ifcanput(row, i, arr)) { //调用iscanput()方法检测该处位置是否合法,合法置为0,用来表示已经放置了皇后;
arr[row][i] = 0;
}else {
continue; //不合法则继续查看此行的下一个位置是否合法;
}
putQueen(row+1, col, arr); //进入下一行(递归)
arr[row][i] = 1; //函数回调(回溯)时,将前一次的记录还原,以便继续探测
}
}
3、以下是完整代码:
package lanqiaobei;
import java.util.Scanner;
public class queenOfeight {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner inputScanner = new Scanner(System.in);
int n = inputScanner.nextInt();
int[][] arr = new int[n][n];
for(int i = 0;i<n;i++) {
for(int j = 0;j<n;j++) {
arr[i][j] = inputScanner.nextInt();
}
System.out.println();
}
putQueen(0,0, arr);
System.out.println(res);
}
//判断该位置是否可以放置皇后
public static boolean ifcanput(int row,int col,int[][] arr) {
// 一行是否有皇后
for(int i =0;i<arr.length;i++) {
if(arr[row][i] == 0) {
return false;
}
}
// 一列是否有皇后
for(int i = 0;i<arr.length;i++) {
if(arr[i][col] == 0) {
return false;
}
}
// 对角线是否有皇后
for(int i = 1;i<arr.length;i++) {
//右下
if(col + i <arr.length &&row + i < arr.length) {
if(arr[row+i][col+i] == 0) {
return false;
}
}
// 左上
if(col - i >= 0 && row - i >= 0) {
if(arr[row - i][col-i] == 0) {
return false;
}
}
//左下
if(row + i < arr.length && col - i >= 0) {
if (arr[row+i][col-i] == 0) {
return false;
}
}
//左上
if (row - i >= 0 && col + i < arr.length) {
if (arr[row-i][col+i] == 0) {
return false;
}
}
}
return true;
}
//放置皇后
static int res = 0; //全局变量
public static void putQueen(int row,int col , int[][] arr) {
if(row == arr.length) {
res ++;
return;
}
//判断一行的每一列是否都可以放置皇后
for(int i = 0;i<arr.length;i++) {
if(arr[row][i] != 1 ) {
continue;
}
if (ifcanput(row, i, arr)) {
arr[row][i] = 0;
}else {
continue;
}
putQueen(row+1, col, arr);
arr[row][i] = 1;
}
}
}
4、运行结果:
二、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
样例输入
4
1 0 1 1
1 1 1 1
1 1 1 1
1 1 1 1
样例输出
0
部分代码:(和八皇后类似)
1、判断该位置是否可以放置皇后:ifcanput()
代码如下:
//判断该位置是否可以放置皇后
public static boolean ifcanput(int row,int col,int[][] arr,int color) {
// 一行是否有皇后
for(int i =0;i<arr.length;i++) {
if(arr[row][i] == color) {
return false;
}
}
// 一列是否有皇后
for(int i = 0;i<arr.length;i++) {
if(arr[i][col] == color) {
return false;
}
}
// 对角线是否有皇后
for(int i = 1;i<arr.length;i++) {
//右下
if(col + i <arr.length &&row + i < arr.length) {
if(arr[row+i][col+i] == color) {
return false;
}
}
// 左上
if(col - i >= 0 && row - i >= 0) {
if(arr[row - i][col-i] == color) {
return false;
}
}
//左下
if(row + i < arr.length && col - i >= 0) {
if (arr[row+i][col-i] == color) {
return false;
}
}
//左上
if (row - i >= 0 && col + i < arr.length) {
if (arr[row-i][col+i] == color) {
return false;
}
}
}
return true;
}
该问题判断是否合法与八皇后问题不同的是:判断颜色是否是一致;
2、开始放置皇后方法:putQueen()
代码如下:
//放置皇后
static int res = 0; //全局变量
public static void putQueen(int row,int col , int[][] arr,int color) {
if(row == arr.length) {
if(color == 2) {
putQueen(0, 0, arr, 3); //此处用2和3分别黑和白象棋;如果黑象棋已经放置好了,开始放置白象棋;
}else {
res ++;
}
return;
}
//判断一行的每一列是否都可以放置皇后
for(int i = 0;i<arr.length;i++) {
if(arr[row][i] != 1 ) {
continue;
}
if (ifcanput(row, i, arr, color)) {
arr[row][i] = color; //将该位置置为所带的颜色;
}else {
continue;
}
putQueen(row+1, col, arr,color);
arr[row][i] = 1;
}
return;
}
3、以下是完整代码:
package lanqiaobei;
import java.util.Scanner;
public class queenOftwon {
public static void main(String[] args) {
// TODO Auto-generated method stub
Scanner inputScanner = new Scanner(System.in);
int n = inputScanner.nextInt();
int[][] arr = new int[n][n];
for(int i = 0;i<n;i++) {
for(int j = 0;j<n;j++) {
arr[i][j] = inputScanner.nextInt();
}
}
putQueen(0, 0, arr, 2);
System.out.println(res);
}
//判断该位置是否可以放置皇后
public static boolean ifcanput(int row,int col,int[][] arr,int color) {
// 一行是否有皇后
for(int i =0;i<arr.length;i++) {
if(arr[row][i] == color) {
return false;
}
}
// 一列是否有皇后
for(int i = 0;i<arr.length;i++) {
if(arr[i][col] == color) {
return false;
}
}
// 对角线是否有皇后
for(int i = 1;i<arr.length;i++) {
//右下
if(col + i <arr.length &&row + i < arr.length) {
if(arr[row+i][col+i] == color) {
return false;
}
}
// 左上
if(col - i >= 0 && row - i >= 0) {
if(arr[row - i][col-i] == color) {
return false;
}
}
//左下
if(row + i < arr.length && col - i >= 0) {
if (arr[row+i][col-i] == color) {
return false;
}
}
//左上
if (row - i >= 0 && col + i < arr.length) {
if (arr[row-i][col+i] == color) {
return false;
}
}
}
return true;
}
//放置皇后
static int res = 0; //全局变量
public static void putQueen(int row,int col , int[][] arr,int color) {
if(row == arr.length) {
if(color == 2) {
putQueen(0, 0, arr, 3);
}else {
res ++;
}
return;
}
//判断一行的每一列是否都可以放置皇后
for(int i = 0;i<arr.length;i++) {
if(arr[row][i] != 1 ) {
continue;
}
if (ifcanput(row, i, arr, color)) {
arr[row][i] = color;
}else {
continue;
}
putQueen(row+1, col, arr,color);
arr[row][i] = 1;
}
return;
}
}
4、运行结果:
总结:
2n皇后问题和八皇后问题类似,不同的地方是该问题需要考虑两种颜色棋子,即先放完一种颜色的棋子再放置另一种颜色的棋子.;每个问题中的两种方法有些许不同,但思路一致。