背景介绍

骑士旅行问题想必大家都很熟悉了吧

这道题的众人向做法就是深度搜索(直接给出一个很C_T风格的代码):

#include<iostream>
#include<iomanip>using namespace std;int zz[8][2]={{1,2},{2,1},{1,-2},{-2,1},{-1,2},{2,-1},{-1,-2},{-2,-1}};
int a[8][8]={0},tot=0,_x,_y;void print() {cout<<++tot<<endl;for (int i=0;i<8;i++) { for (int j=0;j<8;j++) cout<<setw(3)<<a[i][j];cout<<endl;}return;
}void doit(int x,int y,int t) {if (t>64) {print();return;}for (int i=0;i<8;i++) {int xx=x+zz[i][0];int yy=y+zz[i][1];if ((xx>=8)||(xx<0)||(yy>=8)||(yy<0)||(a[xx][yy])) continue;a[xx][yy]=t;doit(xx,yy,t+1);a[xx][yy]=0;}
}int main()
{cout<<"Enter the initial position: ";cin>>_x>>_y;a[_x][_y]=1;doit(_x,_y,2);system("pause");return 0;
}

下面的代码找到一个可行方案就停止搜索(但是在得到可行解之后的回溯过程意外的有些慢):

#include<iostream>
#include<iomanip>using namespace std;int zz[8][2]={{1,2},{2,1},{1,-2},{-2,1},{-1,2},{2,-1},{-1,-2},{-2,-1}};
int a[8][8]={0},tot=0,_x,_y;
bool flag=1;void print() {for (int i=0;i<8;i++) { for (int j=0;j<8;j++) cout<<setw(3)<<a[i][j];cout<<endl;}return;
}void doit(int x,int y,int t) {if (t>64) {flag=0;print();return;}for (int i=0;i<8&&flag;i++) {int xx=x+zz[i][0];int yy=y+zz[i][1];if ((xx>=8)||(xx<0)||(yy>=8)||(yy<0)||(a[xx][yy])||(!flag)) continue;a[xx][yy]=t;doit(xx,yy,t+1);a[xx][yy]=0;}
}int main()
{cout<<"Enter the initial position: ";cin>>_x>>_y;a[_x][_y]=1;doit(_x,_y,2);system("pause");return 0;
}

总的来说,这是一种很暴力的算法,相当于无优先级穷举所有可能路径
这就会带来一个问题:有些起始位置以当前的穷举顺序是无法在有限时间内得到可行解的
以前小憨憨没有考虑过这个问题,直到C先生不断睿智发问,我才意识到这几年我一直停留在众人向做法毫无进步
所以今天,C_T要带来一种更加优秀的做法!


透试算法研究

现在我们希望开发一个移动骑士的试探策略
试探不一定成功,但认真设计的试探方法能提高成功的机会
显然,外层格子比在棋盘中央的壳子更难移动,实际上,最难访问的是四角的格子

这里所谓的 ” 难访问 “ ,实际上就是相比较其他格子,有更少的格子能够到达该处
反过来说,就是从这个格子出发走一步能够到达的格子数更少

嗯?不太明白吗?
下面给出一个棋盘,每一格上标记能访问的格数(盲猜有助于理解)
我叫ta前进策略图(好正常的名字,不像我的一贯作风),格子上的值我就叫ta前进可能值

2      3      4      4      4      4      3      23      4      6      6      6      6      4      34      6      8      8      8      8      6      44      6      8      8      8      8      6      44      6      8      8      8      8      6      44      6      8      8      8      8      6      43      4      6      6      6      6      4      32      3      4      4      4      4      3      2

凭直觉,应先将骑士移动到最难到达的格子,在旅行即将结束时再访问最容易到达的格子,这样成功的机率较高
意识流一下就可以发现,骑士起始时的路径选择应该尽可能少一点
因为在后面试探失败进行回溯的时候,如果起始时的路径选择较多,回溯引发的搜索时间代价会大幅上升

我们可以开发一个访问性试探,将每格进行访问性分类,然后总是设法把骑士移到最难访问的点

一句话总结:任何时候,总是设法将骑士移动到最难访问的点

从这句话中,我们就可以看出这个策略要维护的重点:
每走一步,我们就需要重新维护一遍前进策略图
并且每次在枚举策略的时候,按照该点周围前进可能值由小到大的顺序进行枚举

我在说些什么泡泡茶壶!!!!还是画图告诉大家吧!

然后可能就会有人问了,这三个 " 8 " 有没有什么先后顺序呢?
实际上应该可以有的,毕竟一号8和三号8比二号8更靠近边缘,选择一号8或者三号8,对于之后的选择可能更加有利,
但是如果这样区别的话,就牵扯到处理下一阶段的前进策略图
整个问题的难度就会大幅上升,甚至与之前我们想要降低复杂度的初衷背道而驰,所以我们在这里初步简单的认为,这三个8先选择哪个都没有区别

那么我们的编程思路终于浮出水面了!!!!

在搜索函数中,
首先依据当前的前进策略图得到 ( x , y ) 处的最优前进策略
按照最有前进策略前进后,维护最新的前进策略图,之后进入下一层搜索

在确定这个算法时,我经历了痛苦的思维地狱:到底应该先维护前进策略图还是先找到最优前进策略?


显然,如果一个位置已访问,那么该点的前进可能值就可以视为正无穷(优先级最低)
但是我们还要保证能成功回溯,所以不能在初始数组上直接修改,需要复制地图
如果我头铁直接在原数组上修改,并且取消回溯过程,就相当于认定了在这种类似贪心的算法下一定能找到可行解,不过我怎么证明这种算法的解必存在性呢?


就算我们姑且抛开这个问题不考虑(默认正确)
在搜索过程中,先维护前进策略图还是先找到最优前进策略实际上是毫无区别的
但是因为我们初始点是直接进入搜索的,这个初始点的加入实际上打破了之前的前进策略图的
严谨起见,我们还是应该先维护前进策略图
而且维护前进策略图和获得最优策略的操作完全分开会比较清晰一点
但是为了压缩常数,实际上这两种操作可以杂糅一下


TIP

啊啊啊啊啊啊啊啊,我好蠢啊
整个思路的完善C先生帮了我很大的忙,比心心~

一开始我还以为每一次都要全棋盘维护前进策略图,然后C先生告诉我说每走一步只会影响到ta周围的可到达点
枚举策略顺序也不用全部抽出来排序,只要找到当前情况下的最优方案就可以了

(我真的是学了很多年竞赛的OIer嘛,被踩了啊TwT)

下面给出的是舍弃回溯的不完善代码,但是在博主的实践下,发现除了 ( 3 , 4 ) ( 4 , 3,) 点,其他点都可以靠这种 “ 贪心 ” 一次成功

#include<iostream>
#include<iomanip>using namespace std;const int INF=0x33333333;
int zz[8][2]={{1,2},{2,1},{1,-2},{-2,1},{-1,2},{2,-1},{-1,-2},{-2,-1}};
int strategy_map[8][8]={{2,3,4,4,4,4,3,2},{3,4,6,6,6,6,4,3},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{3,4,6,6,6,6,4,3},{2,3,4,4,4,4,3,2}};
int ans[8][8]={0},_x,_y;
bool flag=1;void print() {for (int i=0;i<8;i++) { for (int j=0;j<8;j++) cout<<setw(3)<<ans[i][j];cout<<endl;}return;
}int update(int x,int y) {int mn=8,pos=9;strategy_map[x][y]=INF;for (int i=0;i<8;i++) {int xx=x+zz[i][0];int yy=y+zz[i][1];if ((xx<0)||(xx>=8)||(yy<0)||(yy>=8)||ans[xx][yy]) continue;strategy_map[xx][yy]--;if (strategy_map[xx][yy]<mn) mn=strategy_map[xx][yy],pos=i;}return pos;
}void doit_try(int x,int y,int t) {if (t>64) {flag=0;print();return;}int num=update(x,y);if (num==9) return;int xx=x+zz[num][0];int yy=y+zz[num][1];ans[xx][yy]=t;doit_try(xx,yy,t+1);  //省略回溯return;
}int main()
{cout<<"Enter the initial position: ";cin>>_x>>_y;ans[_x][_y]=1;doit_try(_x,_y,2);system("pause");return 0;
}

之后我和C先生又花了将近两天的时间研发出了回溯算法
主要还是C先生的功劳啦,谢谢大大对我不离不弃TwT
这里对于我们的思路再罗嗦两句~

回溯问题的难点就在于前进策略图的维护
我一开始的思路被INF的设定堵塞了
实际上,可以通过ans数组直接把这些点踢出讨论组
编辑一个update数组搞定更新地图的任务
之后我们可以再增加一个数组,记录8个方向的可通行性
这样在回溯get新的方向的时候,就可以排除掉之前的方案,选择剩下方案中的最优解

献!丑!了!

#include<iostream>
#include<iomanip>using namespace std;const int INF=0x33333333;
int strategy_map[8][8]={{2,3,4,4,4,4,3,2},{3,4,6,6,6,6,4,3},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{4,6,8,8,8,8,6,4},{3,4,6,6,6,6,4,3},{2,3,4,4,4,4,3,2}};
int zz[8][2]={{1,2},{2,1},{1,-2},{-2,1},{-1,2},{2,-1},{-1,-2},{-2,-1}};
int ans[8][8]={0},_x,_y,tot;void print() {for (int i=0;i<8;i++) { for (int j=0;j<8;j++) cout<<setw(3)<<ans[i][j];cout<<endl;}cout<<endl;return;
}void update(int x,int y,int z) {for (int i=0;i<8;i++) {int xx=x+zz[i][0];int yy=y+zz[i][1];if ((xx<0)||(xx>=8)||(yy<0)||(yy>=8)) continue;strategy_map[xx][yy]+=z;}return;
}int get_num(int x,int y,bool acc[]) {int mn=8,pos=9;for (int i=0;i<8;i++) {if (!acc[i]) continue;int xx=x+zz[i][0];int yy=y+zz[i][1];if ((xx<0)||(xx>=8)||(yy<0)||(yy>=8)||ans[xx][yy]) continue;//注意这里的前提条件有ans[xx][yy]==0if (strategy_map[xx][yy]<mn) mn=strategy_map[xx][yy],pos=i;}return pos;
}void doit_try(int x,int y,int t) {if (t>64) {tot--;print();return;}bool accessible[8];        //方向可通行性memset(accessible,1,sizeof(accessible));update(x,y,-1);            //更新地图int num=get_num(x,y,accessible);      //get最优策略while (num!=9) {int xx=x+zz[num][0];int yy=y+zz[num][1];accessible[num]=0;     //之后即使发生回溯也不可能选择这个方向了ans[xx][yy]=t;doit_try(xx,yy,t+1);if (!tot) return;ans[xx][yy]=0;num=get_num(x,y,accessible);     //get最优策略}update(x,y,1);             //恢复地图进行回溯return;
}int main()
{cout<<"Enter the initial position: ";cin>>_x>>_y;cout<<"Methods number: ";cin>>tot;ans[_x][_y]=1;doit_try(_x,_y,2);system("pause");return 0;
}

骑士旅行问题的试探性算法研究相关推荐

  1. 水面无人艇局部危险避障算法研究 第1章 绪论

    水面无人艇局部危险避障算法研究 Local Risk Obstacle Avoidance Algorithm of USV 博主 的硕士毕业论文 第1章 绪论 1.1 引言 随着世界人口的不断增长以 ...

  2. 经典算法研究系列:二、Dijkstra 算法初探

    经典算法研究系列:二.Dijkstra 算法初探  July   二零一一年一月 ====================== 本文主要参考:算法导论 第二版.维基百科. 写的不好之处,还望见谅. 本 ...

  3. 基于图机器学习的微生物网络关系预测算法研究

    龙亚辉预答辩公告 浏览次数:410日期:2021-03-19编辑:院研究生秘书 预答辩公告 论文题目 基于图机器学习的微生物网络关系预测算法研究 答辩人 龙亚辉 指导教师 骆嘉伟 答辩委员会 主席 王 ...

  4. 多倍体单体型组装算法研究

    多倍体单体型组装算法研究 喻昕   [摘要]:人类已知的疾病都与基因有着直接或者间接的联系,研究不同个体间基因序列的差异对于了解人类的遗传,以及预防疾病等方面都有着重要的作用.SNP是单核苷酸多态性, ...

  5. 基于癌症基因组学数据的miRNA 功能模块识别算法研究

    题目: 基于癌症基因组学数据的miRNA 功能模块识别算法研究 摘要: 大量研究表明miRNA 的异常表达与癌症的发生.发展有关,且miRNA 通常以组合的 方式发挥其协同调控作用.因此,研究miRN ...

  6. 近期活动盘点:心电数据标注系统和深度学习诊断算法研究、2019年第六届清华大学大数据社会科学讲习班...

    想知道近期有什么最新活动?大数点为你整理的近期活动信息在此: 第四期医工结合研讨会:心电数据标注系统和深度学习诊断算法研究 2019年7月11日 7月11日,"医工结合系列研讨会第四期会议: ...

  7. 《基于压缩传感的匹配追踪重建算法研究》读书笔记

    基于压缩传感的匹配追踪重建算法研究 1.压缩感知与传统数据获取和处理过程比较: 压缩感知理论表明,在对信号获取的同时,就对数据进行适当的压缩. 传统的数据获取和处理过程主要包括:采样.压缩.传输.解压 ...

  8. 图像滤镜艺术---图像滤镜晕影调节算法研究

    原文:图像滤镜艺术---图像滤镜晕影调节算法研究 本文对滤镜中常见的晕影,晕角效果的实现做了研究,具体如下: 1 晕影调整算法 所谓晕影就是给图像四个角添加暗影效果,这暗影向图像中心逐渐淡化.我们使用 ...

  9. 图像和视频的快速去雾算法研究

    王昕, 孙莹莹, 李影昉. 图像和视频的快速去雾算法研究[J]. 影像科学与光化学, 2016, 34(1): 82-87.   WANG Xin, SUN Yingying, LI Yingfang ...

最新文章

  1. IFeature接口
  2. 【R语言-20行代码】牛顿迭代法求伽马函数极大似然估计法的参数估计
  3. 使用site-maven-plugin在github上搭建公有仓库
  4. pb 放弃数据窗口所做修改_为什么我放弃在线数据课程进行基于项目的学习
  5. 最全Linux安装Redis最新版
  6. 微信登录画面_微信登录界面改版,美炸了!
  7. 未能找到主机服务器是什么鬼,未能找到指定主机服务器是什么意思
  8. 北部湾及涠洲岛海域潮汐和海平面特征
  9. 开发者如何在 DoraHacks 上参加 Web3世界的 Hackathon 和 Grant
  10. 关于将EXCEL文件导入到MYSQL数据库的一些方法
  11. js 模拟浏览器的并行请求限制
  12. 聚焦品牌化 Morketing2018第二届跨境出口电商营销峰会成功举办
  13. 三王乱战孙宇晨背后:我们需要一场的区块链真理大辩论
  14. jQuery学习-day03
  15. Android常用开源库整理汇总
  16. 淘宝API系列(数据结构对象)
  17. S4 HANA BP-客商共用编码处理(示例:已存在的供应商编码扩展客户数据)
  18. python .net web开发对比_Python与C#/.NET——在使用Python开发大型web应用程序时,需要考虑哪些关键区别?...
  19. CDUT可视化登录查成绩(tkinter+selenium)待更新优化
  20. 用Qt搭建图书管理系统 (十四)

热门文章

  1. 通过jenkins构建服务,并发布服务,修改Jenkins以Root用户运行
  2. 个人作业2——英语学习APP案例分析
  3. 利用纯真IP库建立mysql ip数据库
  4. 基层社会治理综合管理平台智慧街道Java商业源码
  5. 【图】上海居住证办理指南
  6. 翻译Houdini官方文档:PDG教程之HDA处理器
  7. MySQL官方示例数据库的使用
  8. 计算机硬件技术基础李云,2013《数据库技术与应用》实验指导书.pdf
  9. Latex编辑IEEE论文入门问题集锦
  10. 投稿时Cover Letter的重要性(部分体会来自导师)