八皇后问题递归实现

八皇后的问题大家应该都不陌生,问题简单描述如下:

在8*8的国际象棋棋盘上,摆放8个皇后,使它们两两间不能互相攻击(不能在同一行,同一列,同一对角线)。

上学时,用的回溯的算法解决,今天重新思考这个问题,发现用递归也很容易实现。

分析:要在n*n的棋盘上摆放n个皇后,则至少每一行,每一列有且只有一个皇后。问题解决的必要条件是在前n-1列的棋盘上要摆放n-1个皇后。再检查每n列的每一格:在该位置上摆放皇后在前面n-1列得到的解,测试是否皇后间产生攻击,即可。依此类推,最后问题转换成在棋盘的第一列上摆放1个皇后,显然,解法有n种。

以XiYi、XjYj表示两个皇后的位置坐标,两个皇后能攻击的条件:
  • 在同一行:纵坐标相同,即Yi=Yj
  • 在同一列:横坐标相同,即Xi=Xj
  • 在同一正对角线:所有的正对角线为一组线性方程为x+y=N(0<=N<8)的直线,所以Xi+Yi=Xj+Yj=N
  • 在同一副对象线:所有的副对角线为一组线性方程为x-y=N(0<=N<8)的直线,所以Xi-Yi=Xj-Yj=N
直接上代码:
package queen8;

import java.util.ArrayList;
import java.util.List;


public class EightQueenTest {

	public static void main(String[] args) {
		print(listQueue(8, 8));
	}
	
	/**
	 * 在n*n的棋盘,前column列摆放皇后column个皇后的所有解法
	 * @param n	棋盘边长
	 * @param column 列数/皇后数
	 */
	public static List<int[][]> listQueue(int n, int column) {
		List<int[][]> result = new ArrayList<>();
		// 只有一列的情况,显然共row种解法
		if (1 == column) {
			for (int i = 0; i < n; i++) {
				int[][] arr  = new int[n][n]; 
				arr[i][0] = 1;
				result.add(arr);
			}
		} else {
			List<int[][]> subResult = listQueue(n, column - 1);
			// 遍历第column列,第1行到第n行,n格,与column-1列的所有解法比较,看是否是一个解
			for (int i = 0; i < n; i++) {
				for (int j = 0; j < subResult.size(); j++) {
					int[][] arr = subResult.get(j);
					int[][] testArr = test(arr, n, column - 1, i, column - 1);
					if (null != testArr) {
						result.add(testArr);
					}
				}
			}
		}
		return result;
	}
	
	/**
	 * 判断坐标为[row, column]的皇后与arr中皇后是否产生攻击,是则返回null,反之,则返回解
	 * @param arr
	 * @param n
	 * @param m
	 * @return
	 */
	public static int[][] test(int[][] arr, int n, int m, int row, int column) {
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				if (1 == arr[i][j] && attacked(n, i, j, row, column)) {
					return null;
				}
			}
		}
		int[][] arrcp = new int[n][n];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				arrcp[i][j] = arr[i][j];
			}
			arrcp[row][column] = 1;
		}
		return arrcp;
	}
	
	/**
	 * 判断坐标为[row1, column1]的[row2, column2]的皇后能否相互攻击到对方 
	 * @param row1
	 * @param column1
	 * @param row2
	 * @param column2
	 * @return
	 */
	public static boolean attacked(int n, int row1, int column1, int row2, int column2) {
		if (row1 == row2) {// 同一行
			return true;
		} else if (column1 == column2) {// 同一列
			return true;
		} else {// 同一对角线(正对角线满足y=x,副对角线满足x+y=n)
			return (row1 + column1 == row2 + column2)
					|| 
					(row1 - column1 == row2 - column2);
		}
	}
	
	public static void print(List<int[][]> result) {
		System.out.println("总共有" + result.size() + "种解法。");
		for (int i = 0; i < result.size(); i++) {
			System.out.println("第" + (i + 1) + "种解法:");
			int[][] arr = result.get(i);
			for (int j = 0; j < arr.length; j++) {
				int[] subArr = arr[j];
				for (int k = 0; k < subArr.length; k++) {
					System.out.print(subArr[k]==1? "W" : "-");
				}
				System.out.println();
			}
		}
	}
}

最终算得8皇后是92种解法,当把棋盘扩大到13时,程序运行内存溢出了。。。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值