java学习笔记之马踏棋盘算法
马踏棋盘或骑士周游问题
1、马踏棋盘算法也被称为骑士周游问题
2、将马随机放在国际象棋的 8×8 棋盘 Board[0~7][0~7]的某个方格中,马按走棋规则(马走日字)进行移动。要求每个方格只进入一次,走遍棋盘上全部 64 个方格
思路
会使用到深度优先思想和类似迷宫问题的寻路策略问题,和八皇后问题也有相似。
1、用一个二维数组建立整张棋盘。用另外一个二维数组保存棋盘的每一个位置是否走过
2、马在棋盘上有一个初始位置,将这个位置设为已走过,并将步数设为1.
3、获得在这个位置上,马下一步能走的位置集合。
4、遍历集合里的所有位置,如果那个位置没走过,下一步(步数+1)就走它(递归)
5、设置递归结束的标志.用一个布尔变量标志游戏是否成功。当游戏成功时,步数应该等于棋盘格子数。假如某一次,马走完了所有能走的下一步位置,步数还小于棋盘格子数并且还没成功,说明这个位置不能成功的完成游戏,就把这个位置恢复原样(棋盘设为0,设为未走过),接下来的递归会重新去寻找合适的路。如果步数等于棋盘总格子数,说明游戏成功,把标志的布尔变量设为true,这样在层层返回时就不会再进入上面的条件,递归就会逐渐结束而不会深入下去。
涉及到的方法:
根据此时的位置,判断马接下来能走的位置集合。
x的值代表列而y的值代表行
马是按照日字走的,所有当它在中间时最多有8种位置可以走,一 一判断那个位置是否超过棋盘边界。
每种可能都是if,而不是if-else if,因为要获得所有的可能性,而不是找出一个
假如list时一定要新建一个坐标,不能使用同一个,不然值就会互相影响
/** * 根据现在的坐标返回可以走的坐标 x列y行 * * @param current * @return */ public static ArrayList<Point> findWay(Point current) { ArrayList<Point> res = new ArrayList<>(); //可以走的坐标 Point p = new Point(); //5 if ((p.x = current.x - 2) >= 0 && (p.y = current.y - 1) >= 0) { res.add(new Point(p)); } //6 if ((p.x = current.x - 1) >= 0 && (p.y = current.y - 2) >= 0) { res.add(new Point(p)); } //7 if ((p.x = current.x + 1) < X && (p.y = current.y - 2) >= 0) { res.add(new Point(p)); } //0 if ((p.x = current.x + 2) < X && (p.y = current.y - 1) >= 0) { res.add(new Point(p)); } //1 if ((p.x = current.x + 2) < X && (p.y = current.y + 1) < Y) { res.add(new Point(p)); } //2 if ((p.x = current.x + 1) < X && (p.y = current.y + 2) < Y) { res.add(new Point(p)); } //3 if ((p.x = current.x - 1) >= 0 && (p.y = current.y + 2) < Y) { res.add(new Point(p)); } //4 if ((p.x = current.x - 2) >= 0 && (p.y = current.y + 1) < Y) { res.add(new Point(p)); } return res; }
马塔棋盘
不能单纯以step < X * Y来判断是否完成游戏,因为递归回溯时步数也会回溯,所以要设置一个变量
/** * 马踏棋盘算法 * * @param chess 棋盘 * @param row 坐标行 * @param col 坐标列 * @param step 步数 */ public static void traversalChessboard(int[][] chess, int row, int col, int step) { //先走一步 chess[row][col] = step; visit[row][col] = true; //下一步能走的地 ArrayList<Point> way = findWay(new Point(col, row)); while (!way.isEmpty()) { //取出一个能走的地方 Point point = way.remove(0); //走下一步 if (!visit[point.y][point.x]) { traversalChessboard(chess, point.y, point.x, step + 1); } } //判断是否完成游戏,如果没完成就要回溯 if (step < X * Y && !finshed) { chess[row][col] = 0; visit[row][col] = false; }else { finshed=true; } }
优化
这样计算效率比较低,算法比较慢。实际上当我们获得下一步可以走的位置数组时是按照固定的56701234顺序排列的,但是这样效率不高,我们在考虑到走下一步时,应该先走对应下一步的可能性最少的那一步,比如如果7的下一步有3种可能,而5的下一步有6种可能,那先7后5的效率会更高。
所以我们可以使用贪心算法对获得的这个步数集合根据他们下一步的可能性进行由小到大的排序。
/** * 贪心算法优化 * @param ps */ public static void sort(ArrayList<Point> ps){ ps.sort(new Comparator<Point>() { @Override public int compare(Point o1, Point o2) { int way1 = findWay(o1).size(); int way2 = findWay(o2).size(); if(way1<way2){ return -1; }else if(way1==way2){ return 0; }else { return 1; } } }); }
对Comparetor.compare(o1, o2)方法的返回值,如果返回的值小于零,则不交换两个o1和o2的位置;如果返回的值大于零,则交换o1和o2的位置。 注意,不论在compare(o1, o2)中是如何实现的(第一种实现方式是 o1-02, 第二种实现方式是 o2 - o1),都遵循上述原则,即返回值小于零,则交换o1和o2的位置;返回值大于零,则不交换o1和o2的位置。 所以,如果采用第一种实现方式,即 o1 - o2, 那么将是升序排序。因为在原始排序中o1在o2的前边,如果o1小于o2,那么o1 - o2小于零,即返回值是小于零,但是小于零是不会交换o1和o2的位置的,所以o1依然排在o2的前边,是升序;如果o1大于o2,那么o1 - o2大于零,即返回值是大于零,大于零是要交换o1和o2的位置的,所以要改变原始排序中o1和o2的位置,那么依然是升序
最终代码
package algorithm; import java.awt.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; /** * 马踏棋盘算法 */ public class HorseChessboard { static int X;//列 static int Y;//行 static boolean[][] visit; static boolean finshed; public static void main(String[] args) { X = 8; Y = 8; visit = new boolean[X][Y]; finshed = false; int[][] chess = new int[X][Y]; long s = System.currentTimeMillis(); traversalChessboard(chess, 2, 0, 1); long e=System.currentTimeMillis(); System.out.println(e-s); for (int i = 0; i < chess.length; i++) { System.out.println(Arrays.toString(chess[i])); } } /** * 马踏棋盘算法 * * @param chess 棋盘 * @param row 坐标行 * @param col 坐标列 * @param step 步数 */ public static void traversalChessboard(int[][] chess, int row, int col, int step) { //先走一步 chess[row][col] = step; visit[row][col] = true; //下一步能走的地 ArrayList<Point> way = findWay(new Point(col, row)); sort(way); while (!way.isEmpty()) { //取出一个能走的地方 Point point = way.remove(0); //走下一步 if (!visit[point.y][point.x]) { traversalChessboard(chess, point.y, point.x, step + 1); } } if (step < X * Y && !finshed) { chess[row][col] = 0; visit[row][col] = false; }else { finshed=true; } } /** * 根据现在的坐标返回可以走的坐标 x列y行 * * @param current * @return */ public static ArrayList<Point> findWay(Point current) { ArrayList<Point> res = new ArrayList<>(); //可以走的坐标 Point p = new Point(); //5 if ((p.x = current.x - 2) >= 0 && (p.y = current.y - 1) >= 0) { res.add(new Point(p)); } //6 if ((p.x = current.x - 1) >= 0 && (p.y = current.y - 2) >= 0) { res.add(new Point(p)); } //7 if ((p.x = current.x + 1) < X && (p.y = current.y - 2) >= 0) { res.add(new Point(p)); } //0 if ((p.x = current.x + 2) < X && (p.y = current.y - 1) >= 0) { res.add(new Point(p)); } //1 if ((p.x = current.x + 2) < X && (p.y = current.y + 1) < Y) { res.add(new Point(p)); } //2 if ((p.x = current.x + 1) < X && (p.y = current.y + 2) < Y) { res.add(new Point(p)); } //3 if ((p.x = current.x - 1) >= 0 && (p.y = current.y + 2) < Y) { res.add(new Point(p)); } //4 if ((p.x = current.x - 2) >= 0 && (p.y = current.y + 1) < Y) { res.add(new Point(p)); } return res; } /** * 贪心算法优化 * @param ps */ public static void sort(ArrayList<Point> ps){ ps.sort(new Comparator<Point>() { @Override public int compare(Point o1, Point o2) { int way1 = findWay(o1).size(); int way2 = findWay(o2).size(); if(way1<way2){ return -1; }else if(way1==way2){ return 0; }else { return 1; } } }); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持猪先飞。
原文出处:https://blog.csdn.net/touteng55/article/details/115804705
相关文章
- 这篇文章主要介绍了如何利用java语言实现经典《复杂迷宫》游戏,文中采用了swing技术进行了界面化处理,感兴趣的小伙伴可以动手试一试...2022-02-01
java 运行报错has been compiled by a more recent version of the Java Runtime
java 运行报错has been compiled by a more recent version of the Java Runtime (class file version 54.0)...2021-04-01- 这篇文章主要介绍了在java中获取List集合中最大的日期时间操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了教你怎么用Java获取国家法定节假日,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下...2021-04-23
- 这篇文章主要介绍了Java如何发起http请求的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-31
- 说起C#和Java这两门语言(语法,数据类型 等),个人以为,大概有90%以上的相似,甚至可以认为几乎一样。但是在工作中,我也发现了一些细微的差别...2020-06-25
- 这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
- 这篇文章主要介绍了java 判断两个时间段是否重叠的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了超简洁java实现双色球若干注随机号码生成(实例代码),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-02
- 这篇文章主要介绍了Java生成随机姓名、性别和年龄的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-01
java 画pdf用itext调整表格宽度、自定义各个列宽的方法
这篇文章主要介绍了java 画pdf用itext调整表格宽度、自定义各个列宽的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-31- 这篇文章主要介绍了java正则表达式判断前端参数修改表中另一个字段的值,需要的朋友可以参考下...2021-05-07
Java使用ScriptEngine动态执行代码(附Java几种动态执行代码比较)
这篇文章主要介绍了Java使用ScriptEngine动态执行代码,并且分享Java几种动态执行代码比较,需要的朋友可以参考下...2021-04-15- 这篇文章主要介绍了Java开发实现人机猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-03
- 这篇文章主要介绍了Java List集合返回值去掉中括号('[ ]')的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-29
Java中lombok的@Builder注解的解析与简单使用详解
这篇文章主要介绍了Java中lombok的@Builder注解的解析与简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-06- 下面小编就为大家带来一篇java中String类型变量的赋值问题介绍。小编觉得挺不错的。现在分享给大家,给大家一个参考。...2016-03-28
Java 8 Stream 的终极技巧——Collectors 功能与操作方法详解
这篇文章主要介绍了Java 8 Stream Collectors 功能与操作方法,结合实例形式详细分析了Java 8 Stream Collectors 功能、操作方法及相关注意事项,需要的朋友可以参考下...2020-05-20- 这篇文章主要介绍了Java线程池中的各个参数如何合理设置操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-19
- 在Java中,我们可以利用多线程来最大化地压榨CPU多核计算的能力,下面这篇文章主要给大家介绍了关于java中多线程与线程池基本使用的相关资料,需要的朋友可以参考下...2021-09-13