八皇后问题和2n皇后问题

八皇后问题和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皇后问题和八皇后问题类似,不同的地方是该问题需要考虑两种颜色棋子,即先放完一种颜色的棋子再放置另一种颜色的棋子.;每个问题中的两种方法有些许不同,但思路一致。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值