36.骑士周游算法及其基于贪心算法的优化

概述

骑士周游算法,叫做“马踏棋盘算法”或许更加直观。在国际象棋8x8的棋盘中,马也是走“日字”进行移动,相应的产生了一个问题:“如果要求马 在每个方格只能进入一次,走遍全部的64个方格需要如何行进?”这就是著名的 骑士周游算法的由来。
在这里插入图片描述

思路

相信大家看到这个问题首先想到就是回溯
马踏棋盘问题(骑士周游问题) 实际上是图的深度优先搜索(DFS)的应用。
如果使用回溯(就是深度优先搜索) 来解决,假如马儿踏了53个点,走到了第53个,坐标(1,0),发现已经走到尽头,没办法,那就只能回退了,查看其他的路径,就
在棋盘上不停的回溯

基于回溯的解决方案

  1. 创建棋盘chessBoard,是一个二维数组;
  2. 将当前位置设置为已经访问,然后根据当前位置,计算马还能走哪些位置,并放入到一个集合中(ArrayList),最多有8个位置,每走一步,就使用step+1;
  3. 遍历arrayList中存放的所有位置,看看哪个可以走通;
  4. 判断马儿是否完成了任务,使用step和应该走的步数(即棋盘格子数-1)比较,如果没有达到数量,则表示没有完成任务,将整个棋盘置0;
    注:马 不同的走法(策略),会得到不同的结果,效率也会有影响(优化)。

代码实现

public class HorseChessBoard {private static int X;//棋盘的列数private static int Y;//棋盘的行数//创建一个数组, 标记棋盘的各个位置是否被访问过private static boolean visited[];//试用一个属性,标记是否棋盘的所有位置都被访问过了private static boolean finished;//如果为true,表示成功public static void main(String[] args) {System.out.println("开始执行骑士周游算法~");//测试X = 8;Y = 8;int row = 1;//马儿初始位置的行,从1开始编号int column = 1;//马儿初始位置的列,从1开始编号//创建棋盘int[][] chessboard = new int[X][Y];visited = new boolean[X*Y];//初始值都是false//测试一下耗时long start = System.currentTimeMillis();traversalCheessBoard(chessboard,row-1,column-1,1);long end = System.currentTimeMillis();System.out.println("共耗时"+(end - start)+"ms");//输出棋盘的最终状况for (int[] rows : chessboard) {for (int step : rows) {System.out.print(step+"\t");}System.out.println();}System.out.println("骑士周游算法结束");}/*** 骑士周游问题算法* @param chessBoard 棋盘* @param row 马儿当前位置的行 从0开始* @param column 马儿当前位置的列 从0开始* @param step 是第几步,初始位置是第1步*/public static void traversalCheessBoard(int[][] chessBoard,int row,int column,int step){chessBoard[row][column] = step;//row = 4; X=8; column=4; 4*8+4=36;visited[row*X+column] = true;//标记该位置已经访问//获取当前位置可以走的下一个位置的集合ArrayList<Point> ps = next(new Point(column, row));//遍历pswhile (!ps.isEmpty()){Point p = ps.remove(0);//取出下一个可以走的位置//判断该点是否已经访问过if(!visited[p.y*X+p.x]){//说明还没访问过traversalCheessBoard(chessBoard,p.y,p.x,step+1);}}//判断马儿是否完成了任务,使用step和应该走的步数(即棋盘格子数-1)比较,//如果没有达到数量,则表示没有完成任务,将整个棋盘置0;//说明: step<X*Y成立的情况有两种//1.棋盘到目前位置,仍然没有走完//2.棋盘处于回溯过程if (step<X*Y&&!finished){chessBoard[row][column]=0;visited[row * X + column] = false;}else {finished = true;}}/*** 根据当前位置(Point) ,计算马儿还能走哪些位置(Point),并放入到一个集合中(ArrayList),最多有八个位置* @param curPoint* @return*/public static ArrayList<Point> next(Point curPoint){//创建一个ArrayListArrayList<Point> ps = new ArrayList<>();//创建一个PointPoint p1 = new Point();//判断马儿下一步是否可以走,若可以,将这个位置放入集合//判断马儿是否可以走  位置5if ((p1.x=curPoint.x-2)>=0 && (p1.y = curPoint.y-1)>=0){ps.add(new Point(p1));}//判断马儿是否可以走  位置6if ((p1.x=curPoint.x-1)>=0 && (p1.y = curPoint.y-2)>=0){ps.add(new Point(p1));}//判断马儿是否可以走  位置7if ((p1.x=curPoint.x+1) < X && (p1.y = curPoint.y-2)>=0){ps.add(new Point(p1));}//判断马儿是否可以走  位置0if ((p1.x=curPoint.x+2) < X && (p1.y = curPoint.y-1)>=0){ps.add(new Point(p1));}//判断马儿是否可以走  位置1if ((p1.x=curPoint.x+2) < X && (p1.y = curPoint.y+1)< Y){ps.add(new Point(p1));}//判断马儿是否可以走  位置2if ((p1.x=curPoint.x+1)<X && (p1.y = curPoint.y+2)<Y){ps.add(new Point(p1));}//判断马儿是否可以走  位置3if ((p1.x=curPoint.x-1)>=0 && (p1.y = curPoint.y+2)<Y){ps.add(new Point(p1));}//判断马儿是否可以走  位置4if ((p1.x=curPoint.x-2)>=0 && (p1.y = curPoint.y+1)<Y){ps.add(new Point(p1));}return ps;}
}

效率分析

采用回溯的方案思路上自然是可行的,那么它的效率究竟如何呢?可以说很不乐观!测算下来差不多要40秒左右,优化的空间很大。
在这里插入图片描述

回溯分析与贪心优化

我们思考可以在此思考一下上面解决方案的是否有可以优化的地方?能否用贪心算法进行优化呢?

  1. 我们获取当前位置,可以走的下一个位置的集合:
    ArrayList ps = next(new Point(column,row));
  2. 需要对ps中所有Point 下一步的所有集合数目进行非递减排序;
    a. 递减是:9,7,6,5,4…
    b. 递增排序:4,5,6,7,8…
    c. 非递减排序: 1,2,2,3,3,4,4,4,4,4,4,4,5,8,10…
    d. 非递增排序: 9,9,9,8,7,5,3…
  3. 如果下一步的选择越少,意味着回溯时的步骤越少,相应的效率也会越高,所以我们应该采用非递减排序,使得回溯的代价尽可能的低。

核心优化代码

我们不妨编写一个方法,根据当前这一步的所有下一步的选择位置,进行非递减排序,以求减少回溯的次数

public static void sort(ArrayList<Point> ps){ps.sort(new Comparator<Point>(){@Overridepublic int compare(Point o1, Point o2) {//获取到o1的下一步的所有位置个数int count1 = next(o1).size();//获取到o2的下一步的所有位置个数int count2 = next(o2).size();if (count1<count2){return -1;}else if (count1==count2){return 0;}else {return 1;}}});}

这样,在上面的回溯算法中,我们可以先对ps进行排序处理,再进行后面的测算

		//获取当前位置可以走的下一个位置的集合ArrayList<Point> ps = next(new Point(column, row));//对ps进行排序,排序的规则就是对ps的所有的Point对象的下一步的位置数目进行非递减排序sort(ps);//遍历pswhile (!ps.isEmpty()){Point p = ps.remove(0);//取出下一个可以走的位置//判断该点是否已经访问过if(!visited[p.y*X+p.x]){//说明还没访问过traversalCheessBoard(chessBoard,p.y,p.x,step+1);}}

效率分析

经过贪心算法的优化后,相同的配置下,测算时间直接降到了50ms,效率比之前提升600倍。还是很可观的提升的。
在这里插入图片描述

小结

本节,先是采用回溯算法对骑士周游问题进行了拆解,而后利用贪心算法对回溯算法进行了优化解决了骑士周游问题。相信借此我们对贪心算法的应用应该都有了更深层次的理解,算法千万条,应用第一条,只有在合适的场景才能发挥出其最大的作用。


关注我,共同进步,每周至少一更。——Wayne

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/150012.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

1147. 段式回文

链接&#xff1a; ​​​​​​1147. 段式回文 题解&#xff1a; class Solution { public:int longestDecomposition(string text) {MOD 1e9 7;if (text.size() < 0) {return 0;}// 初始化26的n次方的表pow26.resize(text.size());pow26[0] 1;for (int i 1; i < …

【C语言】利用数组处理批量数据(字符数组)

前言:前面已经介绍了&#xff0c;字符数据是以字符的ASCII代码存储在存储单元中的&#xff0c;一般占一个字节。由于ASCII代码也属于整数形式&#xff0c;因此在C99标准中&#xff0c;把字符类型归纳为整型类型中的一种。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x…

数据结构——红黑树(详解性质+C++模拟)

文章目录 前言红黑树的概念红黑树的性质红黑树结点的定义红黑树的插入操作1. **按照二叉搜索树的规则插入新结点**2. 检测新节点插入后&#xff0c;红黑树的性质是否遭到破坏 红黑树的验证总结 前言 本篇博客将为大家重点讲述红黑树这一数据结构&#xff0c;讲解其实现的方式即…

One Thread One Loop主从Reactor模型⾼并发服务器

One Thread One Loop主从Reactor模型⾼并发服务器 文章目录 One Thread One Loop主从Reactor模型⾼并发服务器一些补充HTTP服务器Reactor 模型eventfd通用类Any 目标功能模块划分&#xff1a;SERVER模块Buffer模块&#xff1a;编写思路&#xff1a;接口设计&#xff1a;具体实现…

毕设-原创医疗预约挂号平台分享

医疗预约挂号平台 不是尚医通项目&#xff0c;先看项目质量&#xff08;有源码论文&#xff09; 项目链接&#xff1a;医疗预约挂号平台git地址 演示视频&#xff1a;医疗预约挂号平台 功能结构图 登录注册模块&#xff1a;该模块具体分为登录和注册两个功能&#xff0c;这些…

Linux:环境变量、地址空间

目录 一、环境变量 1、什么是环境变量 2、常见的环境变量 3、环境变量相关命令 二、地址空间 1、进程地址空间 2、虚拟地址空间 一、环境变量 1、什么是环境变量 首先先举个环境变量的例子&#xff1a; 我们在Linux中&#xff0c;运行ls、pwd之类的命令&#xff0c;直…

力扣 -- 873. 最长的斐波那契子序列的长度

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int lenLongestFibSubseq(vector<int>& nums) {int nnums.size();unordered_map<int,int> hash;for(int i0;i<n;i){hash[nums[i]]i;}int ret2;vector<vector<int>> dp(n,v…

Python之元组

Python之元组 元组tuple 一个有序的元素组成的集合使用小括号 ( ) 表示元组是不可变对象 tuple(), (), type(()) # 空元组 ((), (), tuple)(1,), (1) # 元组中只有1必须加逗号&#xff0c;否则就是1了 # ((1,), 1)x 1, 2 # 以逗号分隔的内容会形成元组&#xff0c;封装元组x…

DBC配置SecOC属性

关联文章:Autosar基础——车载信息安全SecOC 属性定义的规范详细介绍请参考 ①dbc属性定义 ②Vector DBC属性定义规则 文章目录 一、SecOC简介二、DBC文件中的SecOC属性三、配置SecOC属性设置SecOC的属性设置同步报文的属性设置同步请求报文的属性一、SecOC简介 在车载网络中…

数据分析篇-数据认知分析

一简介 数据认知分析&#xff0c;实际是对数据的整体结构和分布特征进行分析&#xff0c;是对整个数据外在的认识&#xff0c;也是数据分析的第一步。对于数据认知的分析&#xff0c;一般会考虑分散性、位置特性、变量的相关性等&#xff0c;一般会考虑平均数、方差、极差、峰…

朋友圈怎么定点发朋友圈?

微信朋友圈是我们日常生活中常用的社交媒体之一。但有时我们忙碌而可能会忘记发布朋友圈&#xff0c;或是因时间不合适而无法发布。那么&#xff0c;有没有一种方法可以在规定的时间内自动发布朋友圈呢&#xff1f; 当然有啦&#xff01; 定时发朋友圈可以帮助我们在特定时间点…

7.Tensors For Beginneers - Convector Components

介绍协向量时&#xff0c;曾说过它们有点像 行向量&#xff0c; 行向量确实以某种方式代表了协向量&#xff0c; 这里说明一下&#xff1a; 协向量是不变的&#xff1b; 协向量组件是可变的。 协向量不依赖坐标系&#xff0c;协向量的组件取决于坐标系。 当我们说协向量具有组…

Javascript文件上传

什么是文件上传 文件上传包含两部分&#xff0c; 一部分是选择文件&#xff0c;包含所有相关的界面交互。一部分是网络传输&#xff0c;通过一个网络请求&#xff0c;将文件的数据携带过去&#xff0c;传递到服务器中&#xff0c;剩下的&#xff0c;在服务器中如何存储&#xf…

【11】c++设计模式——>单例模式

单例模式是什么 在一个项目中&#xff0c;全局范围内&#xff0c;某个类的实例有且仅有一个&#xff08;只能new一次&#xff09;&#xff0c;通过这个唯一的实例向其他模块提供数据的全局访问&#xff0c;这种模式就叫单例模式。单例模式的典型应用就是任务队列。 为什么要使…

C++(STL容器适配器)

前言&#xff1a; 适配器也称配接器&#xff08;adapters&#xff09;在STL组件的灵活组合运用功能上&#xff0c;扮演着轴承、转换器的角色。 《Design Patterns》对adapter的定义如下&#xff1a;将一个class的接口转换为另一个class的接口&#xff0c;使原本因接口不兼容而…

Python之字符串构造

Python之字符串构造 字符串str 一个个字符组成的有序的序列&#xff0c;是字符的集合使用单引号、双引号、三引号引住的字符序列字符串是不可变对象&#xff0c;是字面常量 Python3起&#xff0c;字符串都是Unicode类型 x abcde使用for循环遍历x的值&#xff0c;打印并查看…

2023 年 Web 安全最详细学习路线指南,从入门到入职(含书籍、工具包)【建议收藏】

第一个方向&#xff1a;安全研发 你可以把网络安全理解成电商行业、教育行业等其他行业一样&#xff0c;每个行业都有自己的软件研发&#xff0c;网络安全作为一个行业也不例外&#xff0c;不同的是这个行业的研发就是开发与网络安全业务相关的软件。 既然如此&#xff0c;那其…

从零开始学习线性回归:理论、实践与PyTorch实现

文章目录 &#x1f966;介绍&#x1f966;基本知识&#x1f966;代码实现&#x1f966;完整代码&#x1f966;总结 &#x1f966;介绍 线性回归是统计学和机器学习中最简单而强大的算法之一&#xff0c;用于建模和预测连续性数值输出与输入特征之间的关系。本博客将深入探讨线性…

解决报错: require is not defined in ES module scope

用node启动mjs文件报错&#xff1a;require is not defined in ES module scope 现象如下&#xff1a; 原因&#xff1a; 文件后缀是mjs, 被识别为es模块&#xff0c;但是node默认是commonjs格式&#xff0c;不支持也不能识别es模块。 解决办法&#xff1a;把文件后缀从.mjs改…

Spring web security

儅使用spring的web security時&#xff0c;默認會轉向自帶的spring security example page。而不會轉向error page。 TODO: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> &l…