问题的提出
在1848年,国际西洋棋棋手马克斯·贝瑟尔提出了这样的一个问题,
在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问一共有多少种摆法。
我第一时间就是想到定义一个二维数组去存储这个棋盘 用1表示皇后的位置 0表示没有空位置
//初始化 棋盘
int[][] arr = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
};
思路:
- 在棋盘中 一行一行的往下走 每一行就放一个 然后判断在当前位置中是否可以放皇后
- 写方法判断当前位置 是否可以放皇后
- 使用回溯 如果当前位置不可以放皇后的话 就返回上一行 以此类推
- 当行数走到第8行时(数组是0-7行)表示已经走到底了 完成了一种摆法
实现
- 写方法判断当前位置 是否可以放皇后
/**
* 判断行列是否可以
*
* @param x 当前的位置的行数
* @param y 当前的位置的列数
* @return true 表示可以放皇后 否则不行
*/
public static boolean rowAndList(int[][] arr, int x, int y) {
arr[x][y] = 0;
//判断行 行不行 int[x][i]
for (int i = 0; i < 8; i++) {
if (arr[x][i] == 1 || arr[i][y] == 1) {
arr[x][y] = 1;
return false;
}
}
arr[x][y] = 1;
return true;
}
/**
* 判断行列是否可以
*
* @param x 当前的位置的行数
* @param y 当前的位置的列数
* @return true 表示可以放皇后 否则不行
*/
public static boolean xie(int[][] arr, int x, int y) {
arr[x][y] = 0;
// 判断右斜 行不行 int[x][y]
for (int i = 0; i < 8; i++) {
if ((x + i) <= 7 && (y + i) <= 7) {
if (arr[x + i][y + i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x - i) >= 0 && (y - i) >= 0) {
if (arr[x - i][y - i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x + i) <= 7 && (y - i) >= 0) {
if (arr[x + i][y - i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x - i) >= 0 && (y + i) <= 7) {
if (arr[x - i][y + i] == 1) {
arr[x][y] = 1;
return false;
}
}
}
arr[x][y] = 1;
return true;
}
- 回溯
for (int i = 0; i < arr[index].length; i++) {
// 将当前位置置为1
arr[index][i] = 1;
// 判断当前位置是否可以放1
if (rowAndList(arr, index, i) && xie(arr, index, i)) {
//方法名 回溯
read(index + 1, arr);
}
arr[index][i] = 0;
}
//终止
return arr;
- 当行数走到第8行时(数组是0-7行)表示已经走到底了 完成了一种摆法
if (index >= arr[0].length) {
/**
* 这里可以将所有情况都打印出来
*/
sum++;
System.out.println("第"+sum+"种");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
System.out.println();
return;
}
源代码
package WanBanXueXi.task.day_11_28;
/**
* 八皇后解法
* 0 1 0 0 0 0 0 0 0
* 0 0 0 1 0 0 0 0 0
* 0 0 0 0 0 1 0 0 0
* 1 0 0 0 0 0 0 1 0
* 0 0 1 0 0 0 0 0 0
* 0 0 0 0 1 0 0 0 0
* 0 0 0 0 0 0 1 0 0
* 0 0 0 0 0 0 0 0 1
* <p>
* 1 0 0 0 0 0 0 0 0
* 0 0 1 0 0 0 0 0 0
* 0 0 0 0 1 0 0 0 0
* 0 0 0 0 0 0 1 0 0
* 0 1 0 0 0 0 0 0 0
* 0 0 0 1 0 0 0 0 0
* 0 0 0 0 0 1 0 0 0
* 0 0 0 0 0 0 0 1 0
* <p>
* 用下标表示 如果有11 那么 1*都不行 *1都不行 22 33 44 55 66 77 88都不行
* 如果有23 那么
* 1.横竖 2* *3 不行
* 2.右斜 将23都加一 不行 34 45 56 78 减一也不行 12
* 3.左斜 (32 41 14)不行 前加1 后减一 或 前减1 后减1
* <p>
* 回溯
* <p>
* 1.穷举法
* 2.随机法
*/
public class EigthQueenAllPossible {
static int sum = 0;
public static void main(String[] args) {
long begin = System.currentTimeMillis();
int[][] arr = {
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
};
read(0,arr);
System.out.println(sum);
System.out.println("总耗时:\t" + (System.currentTimeMillis() - begin) + "ms");
}
public static void read(int index, int[][] arr) {
if (index >= arr[0].length) {
/**
* 这里可以将所有情况都打印出来
*/
sum++;
System.out.println("第"+sum+"种");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
System.out.println();
return;
}
for (int i = 0; i < arr[index].length; i++) {
arr[index][i] = 1;
if (rowAndList(arr, index, i) && xie(arr, index, i)) {
read(index + 1, arr);
}
arr[index][i] = 0;
}
return;
}
/**
* 判断行列是否可以
*
* @param x
* @param y
* @return
*/
public static boolean rowAndList(int[][] arr, int x, int y) {
arr[x][y] = 0;
//判断行 行不行 int[x][i]
for (int i = 0; i < 8; i++) {
if (arr[x][i] == 1 || arr[i][y] == 1) {
arr[x][y] = 1;
return false;
}
}
arr[x][y] = 1;
return true;
}
/**
* 判断斜是否可以
*
* @param x
* @param y
* @return
*/
public static boolean xie(int[][] arr, int x, int y) {
// a[3][2]
arr[x][y] = 0;
//todo 判断右斜 行不行 int[x][y]
for (int i = 0; i < 8; i++) {
if ((x + i) <= 7 && (y + i) <= 7) {
if (arr[x + i][y + i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x - i) >= 0 && (y - i) >= 0) {
if (arr[x - i][y - i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x + i) <= 7 && (y - i) >= 0) {
if (arr[x + i][y - i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x - i) >= 0 && (y + i) <= 7) {
if (arr[x - i][y + i] == 1) {
arr[x][y] = 1;
return false;
}
}
}
arr[x][y] = 1;
return true;
}
}
总结
这种使用回溯 解决的问题 我觉的还是有点难理解的 可以对着程序 慢慢debug 去理解
还有种简单的解法
随机算法
随机生成8个位置的坐标 然后去判断 这一次的结果 是不是成立
我朋友跑了两三个小时也没有跑出来(笑死) (不推荐)
补充n皇后问题 其实只要修改 一些代码就行
N皇后源代码
package WanBanXueXi.task.day_11_28;
/**
* 八皇后解法
* 0 1 0 0 0 0 0 0 0
* 0 0 0 1 0 0 0 0 0
* 0 0 0 0 0 1 0 0 0
* 1 0 0 0 0 0 0 1 0
* 0 0 1 0 0 0 0 0 0
* 0 0 0 0 1 0 0 0 0
* 0 0 0 0 0 0 1 0 0
* 0 0 0 0 0 0 0 0 1
* <p>
* 1 0 0 0 0 0 0 0 0
* 0 0 1 0 0 0 0 0 0
* 0 0 0 0 1 0 0 0 0
* 0 0 0 0 0 0 1 0 0
* 0 1 0 0 0 0 0 0 0
* 0 0 0 1 0 0 0 0 0
* 0 0 0 0 0 1 0 0 0
* 0 0 0 0 0 0 0 1 0
* <p>
* 用下标表示 如果有11 那么 1*都不行 *1都不行 22 33 44 55 66 77 88都不行
* 如果有23 那么
* 1.横竖 2* *3 不行
* 2.右斜 将23都加一 不行 34 45 56 78 减一也不行 12
* 3.左斜 (32 41 14)不行 前加1 后减一 或 前减1 后减1
* <p>
* 回溯
* <p>
* 1.穷举法
* 2.随机法
*/
public class EigthQueenAllPossible {
static int sum = 0;
public static void main(String[] args) {
long begin = System.currentTimeMillis();
//皇后的数量
int N = 8;
int[][] arr = new int[N][N];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = 0;
}
}
read(0,arr);
System.out.println(sum);
System.out.println("总耗时:\t" + (System.currentTimeMillis() - begin) + "ms");
}
public static void read(int index, int[][] arr) {
if (index >= arr[0].length) {
/**
* 这里可以将所有情况都打印出来
*/
sum++;
System.out.println("第"+sum+"种");
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[i][j] + "\t");
}
System.out.println();
}
System.out.println();
return;
}
for (int i = 0; i < arr[index].length; i++) {
arr[index][i] = 1;
if (rowAndList(arr, index, i) && xie(arr, index, i)) {
read(index + 1, arr);
}
arr[index][i] = 0;
}
return;
}
/**
* 判断行列是否可以
*
* @param x
* @param y
* @return
*/
public static boolean rowAndList(int[][] arr, int x, int y) {
arr[x][y] = 0;
//判断行 行不行 int[x][i]
for (int i = 0; i < arr.length; i++) {
if (arr[x][i] == 1 || arr[i][y] == 1) {
arr[x][y] = 1;
return false;
}
}
arr[x][y] = 1;
return true;
}
/**
* 判断斜是否可以
*
* @param x
* @param y
* @return
*/
public static boolean xie(int[][] arr, int x, int y) {
// a[3][2]
arr[x][y] = 0;
//todo 判断右斜 行不行 int[x][y]
for (int i = 0; i < arr.length; i++) {
if ((x + i) <= arr.length-1 && (y + i) <= arr.length-1) {
if (arr[x + i][y + i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x - i) >= 0 && (y - i) >= 0) {
if (arr[x - i][y - i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x + i) <= arr.length-1 && (y - i) >= 0) {
if (arr[x + i][y - i] == 1) {
arr[x][y] = 1;
return false;
}
}
if ((x - i) >= 0 && (y + i) <= arr.length-1) {
if (arr[x - i][y + i] == 1) {
arr[x][y] = 1;
return false;
}
}
}
arr[x][y] = 1;
return true;
}
}