【一笔画完】通关路径算法的Java代码实现V1.0
文章目录
- 前言
- 一、算法分析
- 二、算法设计
- 三、算法实现
- 四、演示(OneStrokeV1.0)
- 五、有待改进
- 1.不符合算法的有穷性,可能无法得到通关的路径
- 2.空间和时间复杂度高,IDEA的资源开销非常大
- 总结
前言
微信在几年前有一个比较火的小游戏,叫“一笔画完”(如图为游戏的第15关)。游戏规则是根据游戏界面,由起点开始,一笔画完所有的格子即为通关。本文章就是通过设计一个Java程序,输入游戏界面的状态,而由代码执行出我们通关的路径。
一、算法分析
首先,通过分析游戏界面,不难得出以下五点:
第一点:游戏界面是一个矩阵,如游戏第15关是一个4行4列(4×4)的矩阵
第二点:每一个格子有三种状态,即无效、走过和未走过
注:以上面的第15关游戏界面为例,把其当做4×4的矩阵,那么第一行的第1个、第3个、第4个和第四行的第1个格子是游戏中用不上的格子,即为无效格子。
第三点:格子最多有四个方向,即向上、向下、向左、向右
第四点:所有的格子均被走过为通关
结论:用穷举法,暴力破解,因为穷举法最符合人脑的思维方式,可以先码出一个程序再说
二、算法设计
经过上述的分析,可以一一对应地设计:
第一步:创建一个二维的整型数组,行数和列数由输入的整数决定,起点也由输入决定
第二步:二维数组的数字由0、1组成,1为无效和走过的格子、0为未走过的格子
第三步:格子的上、下、左、右的移动。在移动前先判断此格子有哪几种可选择的路径方向,然后在可选择的方向中随机选一种,如果没有选择的方向,就重新开始,直至达到第四步的要求。格子的移动采用递归
向上移动:行下标-1、列下标不变向下移动:行下标+1、列下标不变向左移动:行下标不变、列下标-1向右移动:行下标不变、列下标+1
第四步:如果此格子没有可选择的方向且二维数组未走过(0)的格子数量为1,即为通关,打印出路径,即下面这种情况:
三、算法实现
好了,上代码,先创建出几个基本方法:
方法一、int[] Channel(int arr[][], int i, int j):根据传入的2维数组和坐标,返回此格子可选择的方向集合m,代码如下:
/*** 找出此位置有几种选择的方向* @param arr 游戏矩阵* @param i 传入的坐标i* @param j 传入的坐标j* @return 可选择的方向数组,长度为4,分别代表上,下,左,右*/public static int[] Channel(int arr[][], int i, int j){int[] num = new int[4];if(i > 0 && arr[i - 1][j] != 1) { //上num[0] = 1;}if(i < arr.length - 1 && arr[i + 1][j] != 1){ //下num[1] = 1;}if(j > 0 && arr[i][j - 1] != 1) { //左num[2] = 1;}if(j < arr[0].length - 1 && arr[i][j + 1] != 1) { //右num[3] = 1;}return num;}
注:m为一维整形数组,长度为4,对应格子的上、下、左、右四个方向,用0、1填充,1为次格子的该方向可以走,0反之
方法二、boolean isNotChoice(int arr[][]):判断传入的2维矩阵是否存在没有方向选择的格子,有返回True,代码如下:
/*** 判断矩阵中是否存在可选择路径为0的坐标* @param arr 游戏矩阵* @return 有返回false,没有返回true*/public static boolean isNotChoice(int arr[][]){for(int i = 0; i < arr.length; i++){for(int j = 0; j < arr[0].length; j++){if (arr[i][j] == 0 && NotZero(Channel(arr, i, j)) == 0)return true;}}return false;}
注:NotZero()方法作用为找出格子放回的m数组中1(可走)的个数,代码如下:
/*** 找出方向选择数组中可选择方向的数量* @param arr 方向选择数组* @return 返回方向选择数组中可选择方向的数量*/public static int NotZero(int arr[]){int n = 0;for(int i = 0; i < 4; i++){if(arr[i] != 0){n++;}}return n;}
方法三、int Zero(int arr[][]):返回2维数组中0(未走过)的个数,代码如下:
/*** 判断矩阵中0的数量,即未走过的坐标* @param arr 游戏矩阵* @return 返回矩阵中未走过的坐标数量(int)*/public static int Zero(int arr[][]){int end = 0;for(int i = 0; i < arr.length; i++){for(int j = 0; j < arr[0].length; j++){if (arr[i][j] == 0)end++;}}return end;}
方法四:int FindPos(int n, int arr[]):返回产生的随机数在数组中不为零的位置
例:一个格子的方向集合m,产生的随机数n如下
m = {0,1,0,1}(下、右) n = 0[0,2) FindPos(n,m) = 1(下)
m = {0,1,1,1}(下、左、右) n = 2[0,3) FindPos(n,m) = 3(右)
m = {1,0,0,1}(上、右) n = 1[0,2) FindPos(n,m) = 3(右)
代码如下:
/*** 找出方向选择数组中第n个不为0的数* @param n 整型* @param arr 方向选择数组* @return 返回第n个不为0的元素的下标*/public static int FindPos(int n, int arr[]){int m = 0;for(int i = 0; i < arr.length; i++){if(arr[i] == 1)m++;if(m == n + 1) {return i;}}return 0;}
方法五:主方法Start(),代码如下:
/*** 暴力破解开始* @param arr 游戏矩阵* @param i 起点坐标的i值,从0开始* @param j 起点坐标的j值,从0开始* @param map 存储执行路线的字符矩阵* @param blank 无效坐标ID数组* @param start 起点坐标* @param matrix 存储矩阵参数数组* @param count 统计暴力破解次数*/public static void Start(int arr[][], int i, int j, char[][] map, int[] blank, int[] start, int[] matrix, int count){System.out.println("-----------------------");PrintArray(map);//如果矩阵中还存在未走过(0)的格子,就进入循环while(Zero(arr) != 0) {//根据传入的二维矩阵和坐标,计算该格子可选择的方向,返回方向选择集合mint[] m = Channel(arr, i, j);//如果m中可选择(1)的个数大于0,则进行随机选择一个方向进行移动if (NotZero(m) > 0 && !isNotChoice(arr)) {//将此位置置为1,即"走过"arr[i][j] = 1;//根据可选择方向数量,随机选择一个方向,进行移动int random = (int) (Math.random() * NotZero(m));//找出此随机数在方向选择数组m中代表的方向int n = FindPos(random, m);//根据n进行移动if (n == 0) { //上if (i > 0 && arr[i - 1][j] != 1) {map[i][j] = '↑';Start(arr, i - 1, j, map, blank, start, matrix, count);}}if (n == 1) {if (i < arr.length - 1 && arr[i + 1][j] != 1) { //下map[i][j] = '↓';Start(arr, i + 1, j, map, blank, start, matrix, count);}}if (n == 2) {if (j > 0 && arr[i][j - 1] != 1) { //左map[i][j] = '←';Start(arr, i, j - 1, map, blank, start, matrix, count);}}if (n == 3) {if (j < arr[0].length - 1 && arr[i][j + 1] != 1) { //右map[i][j] = '→';Start(arr, i, j + 1, map, blank, start, matrix, count);}}//如果m中可选择(1)的个数等于0,且二维矩阵还仅有一个格子未走过(0),即代表通关,打印路径} else if (NotZero(m) == 0 && Zero(arr) == 1){arr[i][j] = 1;map[i][j] = '●';System.out.println("-----------------------");System.out.println("7.路线图如下:");PrintArray(map);System.out.println("Count:" + (++count));//如果m中可选择(1)的个数等于0,代表走到死胡同,清空二维数组和map数组,重新开始}else if (NotZero(m) == 0 || isNotChoice(arr)){System.out.println("Count:" + (++count));System.out.println("-----------------------");System.out.println("重新开始:");char[][] mapRestart = SetArray(matrix, blank);mapRestart[start[0]][start[1]] = '◎';ClearArray(arr);SetArray(arr, blank);Start(arr, start[0], start[1], mapRestart, blank, start, matrix, count);}}}
注:map[][]是复刻arr[][]二维数组的字符数组,方便观察程序的运行情况
四、演示(OneStrokeV1.0)
以上面的第15关为例,开始演示:
第一步:输入矩阵的行列数
注:第15关是4×4的矩阵,输入:4 4
第二步:输入无效矩阵ID
注:第15关,按照行编号,从0开始,ID为0、2、3、12的格子是无效的,故输入:0 2 3 12
第三步:输入游戏起点坐标
注:从0开始,15关的起点是,第0行,第1列,输入:0 1
运行结果
注:可以看出,通关的路线打印出来了。走了两次错误的路径,Count的值为2,在第三次的时候通关了。
五、有待改进
沿着算法的设计思路下来,不难发现以下几点有待改进的地方:
1.不符合算法的有穷性,可能无法得到通关的路径
算法的有穷性是指算法必须能在执行有限个步骤之后终止;很明显,因为此算法没有排除错误路径的机制,所以按道理来说,运气足够好或者运气足够差,程序都是有可能一直走错误的路径,还可能是重复的。程序之所以能运行出通关路径,是因为15关的矩阵较为简单,路径的变化比较少。如演示的15关,我列举了一下,一共有26种路径变化(如图),其中有4种通关路径,也就是说,程序运行一次得出结果的概率为2/13,理论上运行时间足够长,尝试的次数足够多,是大概率能得出结果的,但实际上也可能一直得不出结果,这就不符合上面说的算法有穷性的“必须能在执行有限个步骤之后终止”,这个算法做不到“必须”,只能做到2/13
2.空间和时间复杂度高,IDEA的资源开销非常大
运行一下6×6的矩阵试一下,问题一下就会暴露出来。如117这关,运行之后,IDEA报错
这是IDEA中java虚拟机中的线程的栈内存太小,满足不了程序递归的层数了,所以报错了。解决这个问题需要设置程序配置中的这个参数,“-Xss128m”,上面这一关我是设置128MB的,如下,运行说明还是不够,可以再调高一点,但这治标不治本。
总结
通过穷举法,先大致码出一个程序,视为V1.0,再分析程序中的问题和改进的方法。需要全部源码的请留言
【一笔画完】通关路径算法的Java代码实现V1.0相关推荐
- 负载均衡算法及其Java代码实现
负载均衡算法及其Java代码实现 什么是负载均衡 负载均衡,英文 名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须 ...
- 对一致性Hash算法,Java代码实现的深入研究
一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...
- java负载均衡原理_多种负载均衡算法及其 Java 代码实现
首先给我们介绍下什么是负载均衡 负载均衡 树立在现有网络结构之上,它供给了一种廉价有用通明的办法扩展 网络设备和 效劳器的带宽.添加 吞吐量.加强网络数据处理才能.进步网络的灵敏性和可用性. 负载均衡 ...
- Java编码规范V1.0
Java编码规范V1.0 1 代码总体原则 1. 清晰第一 清晰性是易于维护.易于重构的程序必需具备的特征.代码首先 是给人读的,其次才给机器用来执行. 目前软件维护期成本占整个生命周期成本的 40 ...
- Java自动化测试系列[v1.0.0][TestNG测试开发环境配置]
基于之前写的一篇文章Java自动化测试系列[v1.0.0][Maven开发环境]的基础上,阐述如何配置单元测试框架TestNG的测试开发环境 创建Maven项目 启动IDEA,点击Create New ...
- 我的世界1 11java,Editing Java版Alpha v1.0.11
Anti-spam check. Do not fill this in!{{version nav |title=Alpha v1.0.11 |edition=java |image=Alpha v ...
- 微信一笔画游戏 的 路径算法
最近 地铁 上玩这个 然后想了想 路径 算法 只会穷举~ function findWay(m, n,/*空点 不能走*/emptyArrs,/*起点*/start) {let totalPoint ...
- 多种负载均算法及其 Java 代码实现 --转
原文地址:https://www.oschina.net/news/81750/variety-pf-load-balancing-algorithm-and-its-java-code 首先给大家介 ...
- java 路由算法_几种简单的负载均衡算法及其Java代码实现
什么是负载均衡 负载均衡,英文 名称为Load Balance,指由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助.通过某种 负载分 ...
最新文章
- keras 的 example 文件 babi_rnn.py 解析
- 以太坊是什么,为什么这么火?
- 【错误记录】NDK 配置错误 ( C/C++ debug|arm64-v8a : Could not get version from cmake.dir path )
- 010 Editor v8.0.1_x32分析以及注册机制作
- 【数据结构与算法】之深入解析“等差数列划分II”的求解思路与算法示例
- 二:java语法基础:
- MFC实现Windows锁屏
- linux nslookup命令安装,在CentOS中安装nslookup命令
- TikTok如何将粉丝转到私域,提高转化和复购?
- JavaBean递归拷贝工具类Dozer
- 基于Matlab的语音识别
- vb.net的socket编程
- 用Python读写Word文档入门
- flash 火狐总是崩溃_火狐浏览器flash插件崩溃怎么办?解决firefox经常出现Adobe Flash 插件已崩溃方法...
- word刷子刷格式_Word文档中用格式刷快速编辑数据格式的方法
- Marvell 88E1111 百兆工程 (FPGA)
- 在Delphi中很精确地控制生成的WORD文档的格式
- JavaScript中的数据类型判断
- 华为扩大内存代码_如何将华为手机带代码加大内存5s?
- vue中集成的ui组件库_Vue组件可使用Vault Flow通过Braintree集成PayPal付款