八皇后问题,是一个古老而著名的问题,是回溯算法的典型例题。
该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
计算机发明后,有多种方法可以解决此问题。

2010年,在写完中国象棋的核心模块后,当时添加了一个扩展应用模块,N皇后问题。

效果图

算法源码

下面是控制台程序的源码。

/**
* 项目名称: FansChineseChess
* 版本号:2.0
* 名字:雷文
* 博客: http://FansUnion.cn
* CSDN:http://blog.csdn.net/FansUnion
* 邮箱: leiwen@FansUnion.cn
* QQ:240-370-818
* 版权所有: 2011-2013,leiwen
*/
package cn.fansunion.chinesechess.ext.empress; import java.awt.Point;
import java.util.ArrayList;
import java.util.List; /**
* 求N皇后的所有合法布局
*
* 3个约束条件:任何2个棋子都不能占居棋盘上的同一行、或者同一列、或者同一对角线。
*
* 棋盘状态的变化情况,可以看作一个N叉树。
*
* 求所有合法布局的过程,即为在3个约束条件下先根遍历状态树的过程。
*
* 遍历中访问结点的操作为,判断棋谱上是否已经得到一个完整的布局,如果是,则输出该布局;
*
* 否则依次先根遍历满足约束条件的各棵子树,即首先判断该子树根的布局是否合法,若合法,则
*
* 先根遍历该子树,否则减去该子树分支。
*
* @author leiwen@fansunion.cn,http://FansUnion.cn,
* http://blog.csdn.net/FansUnion
* @since 2.0
*/
public class EmpressModel { /**
* 皇后的个数
*/
private int num;
/**
* 棋盘数据结构
*/
private int[][] array;
/**
* 棋盘中的布局集合,每一种布局采用简写形式
*/
private List<ArrayList<Point>> lists = new ArrayList<ArrayList<Point>>();
/**
* 棋盘中的布局集合,每一种布局采用完整形式
*/
private List<int[][]> advancedLists = new ArrayList public EmpressModel(int n) {
this.num = n;
array = new int[n + 1][n + 1];// 默认为0
} // 初始化所有的布局
public void initAllLayout() {
trial(1);
sort();
initAdvancedLists();
} /**
* 进入本函数时,在n*n棋盘前j-1列已经放置了满足3个条件的j-1个棋子
*
* 现在从第j列起,继续为后续棋子选择合适的位置
*
* 选择列优先,是为了保证在GUI显示布局的变化,看起来是,每一行的棋子都是从左向右移动的。
*
* 行优先时,每列棋子,从上向下移动。
*
* @param j
*/
private void trial(int j) {
// 进入本函数时,在n*n棋盘前j-1列已经放置了满足3个条件的j-1个棋子
// 现在从第i行起,继续为后续棋子选择合适的位置
if (j > num) {
// 求得一个合法布局,保存起来
saveCurrentLayout();
} else {
for (int i = 1; i <= num; i++) {
// 在第i行第j列放置一个棋子
array[i][j] = 1;
if (isLegal(i, j)) {
trial(j + 1);
}
// 移走第i行第j列的棋子
array[i][j] = 0;
}
}
} /**
* 判断当前布局是否合法
*
* @return
*/
private boolean isLegal(int row, int col) { for (int i = 1; i < array.length; i++) {
int sumI = 0;// 第i行之和
int sumJ = 0;// 第i列之和
for (int j = 1; j < array[i].length; j++) {
sumI += array[i][j];
sumJ += array[j][i];
}
if (sumI >= 2 || sumJ >= 2) {
return false;
}
sumI = 0;
sumJ = 0; } // 左上到右下的对角线是否有棋子
int i = row - 1;
for (int j = col - 1; j >= 1; j--) {
if (i >= 1 && array[i][j] == 1) {
return false;
}
i--;
} // 左下到右上的对角线是否有棋子
i = row + 1;
for (int j = col - 1; j >= 1; j--) {
if (i <= this.num && array[i][j] == 1) {
return false;
}
i++;
} return true;
} /**
* 保存当前的布局
*/
private void saveCurrentLayout() {
ArrayList<Point> list = new ArrayList<Point>();
for (int i = 1; i < array.length; i++) {
for (int j = 1; j < array[i].length; j++) {
if (array[i][j] == 1) {
list.add(new Point(i, j));
}
}
}
lists.add(list);
} /**
* 打印所有的布局(最简形式)
*/
public void printBasicLayout() {
int size = lists.size();
for (int i = 0; i < size; i++) {
ArrayList<Point> arrayList = lists.get(i);
int size2 = arrayList.size();
System.out.println("第" + i + "种布局!");
for (int j = 0; j < size2; j++) {
Point point = arrayList.get(j);
System.out.print("(" + (int) point.getX() + ","
+ (int) point.getY() + ")\t");
}
System.out.println();
}
System.out.println();
} /**
* 打印所有的布局(高级形式)
*/
public void printAddvancedLayout() {
int size = advancedLists.size();
for (int i = 0; i < size; i++) {
int[][] arr = advancedLists.get(i);
System.out.println("第" + i + "种布局!");
for (int j = 1; j <= num; j++) {
for (int k = 1; k <= num; k++) {
System.out.print(arr[j][k] + "\t");
}
System.out.println();
}
System.out.println();
}
System.out.println();
} /**
* 排序是为了,减少抖动,即每次变换布局时,近可能少地换棋子 (每次切换到下一个布局时,棋子的变换尽可能少,视觉上棋子在有规律的移动)
*/
public void sort() {
sortEveryList();
} private void sortEveryList() {
/*
* System.out.println("冒泡排序之前:"); printAllLayout();
*/
int size = lists.size();
// 对lists中的每个链表,按照点的纵坐标排序
for (int q = 0; q < size; q++) { ArrayList<Point> arraylist = lists.get(q); int size2 = arraylist.size();
// 冒泡排序
for (int r = 1; r < size2; r++) {
// 纵坐标从小到大
for (int s = 0; s < size2 - r; s++) {
Point first = arraylist.get(s);
double m = first.getY(); Point second = arraylist.get(s + 1);
double n = second.getY(); if (m > n) {
arraylist.set(s + 1, first);
arraylist.set(s, second);
} }
}
} } private void initAdvancedLists() {
int size = lists.size();
for (int index = 0; index < size; index++) {
ArrayList<Point> list = lists.get(index);
int[][] arr = new int[num + 1][num + 1]; int len = list.size();
for (int i = 0; i < len; i++) {
int x = (int) list.get(i).getY();
int y = (int) list.get(i).getX();
arr[x][y] = 1;
} advancedLists.add(arr);
}
} public int[][] getArray() {
return array;
} public int getNum() {
return num;
} public List<ArrayList<Point>> getLists() {
return lists;
} public List<int[][]> getAdvancedLists() {
return advancedLists;
} public static void main(String[] args) {
EmpressModel em4 = new EmpressModel(4);
em4.initAllLayout();
em4.printBasicLayout();
// 初始化高级保存需要的布局
//em4.initAdvancedLists();
em4.printAddvancedLayout(); } }

设计思想

算法(模型)和界面相分离。

算法构造数据,界面展示数据。

展示分2种:中国象棋棋盘中和控制台中。

源码出处

本算法源码摘自 http://blog.csdn.net/fansunion/article/details/11787413

中国象棋程序-源码,扩展应用模块--N皇后

原文参见:http://FansUnion.cn/articles/2684

中国象棋程序的设计与实现(六)--N皇后问题的算法设计与实现(源码+注释+截图)的更多相关文章

  1. JavaScript中国象棋程序(1) - 界面设计

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第1节. 这一系列共有9个部分: 0.JavaScript中国象 ...

  2. 中国象棋程序的设计与实现(十一)--第2次回答CSDN读者的一些问题

    最近一段时间,有不少CSDN读者朋友看了我写的中国象棋文章.其中,不少爱好者下载了中国象棋程序的初级版和高级版源码. 由于水平有限,不少同学遇到了若干问题,向我咨询,寻找解决办法. 我的处境1.如果我 ...

  3. JavaScript中国象棋程序(0) - 前言

    “JavaScript中国象棋程序” 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.希望通过这个系列,我们对博弈程序的算法有一定的了解.同时,我们也将构建出一个不错的中国象棋程序 ...

  4. JavaScript中国象棋程序(2) - 校验棋子走法

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第2节. 这一系列共有9个部分: 0.JavaScript中国象 ...

  5. JavaScript中国象棋程序(3) - 电脑自动走棋

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第3节. 这一系列共有9个部分: 0.JavaScript中国象 ...

  6. JavaScript中国象棋程序(4) - 极大极小搜索算法

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第4节. 这一系列共有9个部分: 0.JavaScript中国象 ...

  7. JavaScript中国象棋程序(5) - Alpha-Beta搜索

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第5节. 这一系列共有9个部分: 0.JavaScript中国象 ...

  8. JavaScript中国象棋程序(6) - 克服水平线效应、检查重复局面

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第6节. 这一系列共有9个部分: 0.JavaScript中国象 ...

  9. JavaScript中国象棋程序(7) - 置换表

    "JavaScript中国象棋程序" 这一系列教程将带你从头使用JavaScript编写一个中国象棋程序.这是教程的第2节. 这一系列共有9个部分: 0.JavaScript中国象 ...

随机推荐

  1. js 获取对象长度

    获取对象的程度,可以这样获取: var myObj = {a:1,b:2,c:3} var arr = Object.keys(myObj);var len = arr.length  console ...

  2. HDU1061 - Rightmost Digit

    Given a positive integer N, you should output the most right digit of N^N. Input The input contains ...

  3. Link Cut Tree 动态树 小结

    动态树有些类似 树链剖分+并查集 的思想,是用splay维护的 lct的根是动态的,"轻重链"也是动态的,所以并没有真正的轻重链 动态树的操作核心是把你要把 修改/询问/... 等 ...

  4. 命令行导入导出Mysql数据库

    MySQL命令行导出数据库:1,进入MySQL目录下的bin文件夹:cd MySQL中到bin文件夹的目录,如我输入的命令行:cd C:\Program Files\MySQL\MySQL Serve ...

  5. 小学生都能学会的python(生成器)

    小学生都能学会的python(生成器) 1. 生成器 生成器的本质就是迭代器. 生成器由生成器函数来创建或者通过生成器表达式来创建 # def func(): # lst = [] # for i i ...

  6. 单元测试Struts2Spring项目的Action和Service(包含源码)

    最近,认真实践了单元测试Struts2.Spring等Java项目,今天特意写的是单元测试Struts2Spring项目的Action和Service. 由于已经写过不少Web开发框架单元测试的代码, ...

  7. LeetCode 11. Container With Most Water 单调队列

    题意 Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai ...

  8. IIC 原理讲解

    IIC具体是什么这里我就不细说了,只收集一些关于IIC的原理. IIC总线优点是节约总线数,稳定,快速, 是目前芯片制造上非常 流行的一种总线,大多数单片机已经片内集成了IIC总线接口,无 需用户自己 ...

  9. [AngularJS]Chapter 1 AnjularJS简介

    创建一个完美的Web应用程序是很令人激动的,但是构建这样应用的复杂度也是不可思议的.我们Angular团队的目标就是去减轻构建这样AJAX应用的复杂度.在谷歌我们经历过各种复杂的应用创建工作比如:GM ...

  10. POJ 1765 November Rain

    题目大意: 有一些屋顶,相当于一些线段(不想交). 问每一条线段能够接到多少水,相对较低的屋顶能够接到高屋顶留下的水(如题图所看到的).因为y1!=y2,所以保证屋顶是斜的. 解题思路: 扫描线,由于 ...