一、题意

经典的八数码问题,有人说不做此题人生不完整,哈哈。给出一个含数字1~8和字母x的3 * 3矩阵,如:

1  2  X
           3 4  6
           7  5  8

现在要你移动x的位置(方向为上、下、左、右),使得这个矩阵为:

1  2  3 
           4  5  6
           7  8 x

求出最后能得到这个解的移动方案,输出移动的操作。(不要求最优解,也就是不要求移动次数最少)

二、题解

这个8数码问题,我们可以把它看成是一个全排列,从一个初始的排列通过移动元素的位置,到达最终的123456789的这种排列,这里的X我们用9代替。

我们知道X有四个选择移动的方向,但是不一定有四个,如果在左上角就只能右移和下移了,还有很多种限制情况。由于数组是从0开始的,所以他的下标应该是:

0  1  2

3  4  5

6  7  8

if( id % 3 != 2)如果id不是2,5,8就可以向右移动;
     if (id % 3 != 0) 如果id不是0,3,6就可以向左移动;
     if( id > 2 ) 如果id大于2就可以向上移动;
     if( id < 6)  如果id小于6就可以向下移动。
     现在我们有了移动的方向了,但是我们知道可能这些选择中包含着以前就走过的状态。那怎么办来避免已经访问过的状态呢,这就用到了把全排列转换成数字的hash函数(这里其实还可以用康托展开),每一个排列都能对应一个数字,如果这个数字出现过就不访问。这可以用一个Boolean数组实现。

这个hash函数的原理用到了变进制和序数对的知识,详细信息请参考:http://blog.sina.com.cn/s/blog_6635898a0100p4re.html

接下来就要用到BFS来找到状态转换路径了,每一个状态用一个类表示,包括前一个状态到本状态的操作本状态的数本状态X所处位置索引前一个状态的hash值

遍历每个状态从0开始,记录可以到达的状态(一次最多可以到达四个),直到num=123456789。例如,0可以到1、2、3,而1可以到4、5,2可以到6、7,...并用

q[tail].pre = head记录之前的的结点,这样到最后的最终结点往前遍历就可以了。q[tail].op = op记录了操作,输出操作就可以了。这就相当于找到了一条路径,路径上的结点记录了到这一步的操作,输出操作就行了。但想之前所说的,这不一定是最优解,这是最快到达的,不一定是操作最少的。

三、java代码

import java.util.Scanner;class Status{char operation;int number, index, previous;
}public class Main{static int Max = 363000;  //总得状态数量, 9!=362880static Status[] q=new Status[Max];static int head;static int tail;static int factorial[] = {1,1,2,6,24,120,720,5040,40320};static int Pow[] = {100000000,10000000,1000000,100000,10000,1000,100,10,1};static boolean[] vis=new boolean[Max];// 全排列的hash函数。static int permutationToNumberHash(int num){ int i, j;int[] n=new int[10];for(i = 0; i < 9; i ++){n[i] = num % 10;num /= 10;}int c, key = 0;for(i = 1; i < 9; i ++){for(c = 0, j = 0; j < i; j ++)if(n[j] < n[i]) c ++;key += c * factorial[i];}return key;}static void exchangeLocation(int num, int a, int b, char op){ // 操作:第a个数和第b个数交换。int n1, n2;n1 = num / Pow[a] % 10;n2 = num / Pow[b] % 10;num = num - (n1-n2)*Pow[a] + (n1-n2)*Pow[b]; //移动后的数字大小int key = permutationToNumberHash(num);if(!vis[key]){vis[key] = true;q[tail].operation = op;q[tail].number = num;q[tail].previous = head;q[tail ++].index = b;}}static void output(int k){if(q[k].operation != 0){output(q[k].previous);System.out.print( q[k].operation);}}public static void main(String args[]){Scanner sc=new Scanner(System.in);int i, num, id , t;char c;id=-1;//读入数据,用9代替x,用一个十进制数表示读入数据。for(num = i = 0; i < 9; i ++){c=sc.next().charAt(0);if(c == 'x'){t = 9;id = i;}else t = c - '0';num = 10 * num + t;//顺序存储}//初始化每一个状态for(i=0;i<Max;i++){q[i]=new Status();}boolean flag = false;head = 0;tail = 1;//q[0]表示x所在的的id和状态数q[0].index = id;q[0].number = num;//广度搜索找出一条路径,最后的数是123456789./*具体实现:* 遍历每个状态从0开始,记录可以到达的状态(一次最多可以到达四个)。直到num=123456789。* 例如,0可以到1、2、3,而1可以到4、5,2可以到6、7,...并用 q[tail].pre = head记录之前的的结点,这样到最后* 的最终结点往前遍历就可以了。q[tail].op = op记录了操作,输出操作就可以了。* */while(tail > head && !flag){int cnt = tail - head;while(cnt --!=0){num = q[head].number;if(num == 123456789){flag = true; break;}id = q[head].index;//注意这里的移动方向是指数的移动方向,不是X的移动方向//还有id的范围是0~8/* 0 1 2* 3 4 5* 6 7 8*/if(id % 3 != 2)   //如果id不是2,5,8就可以向右移动exchangeLocation(num, id, id + 1, 'r');if(id % 3 != 0)   //如果id不是0,3,6就可以向左移动exchangeLocation(num, id, id - 1, 'l');if(id > 2)        //如果id大于2就可以向上移动exchangeLocation(num, id, id - 3, 'u');if(id < 6)          //如果id小于6就可以向下移动exchangeLocation(num, id, id + 3, 'd');head ++;}}if(flag) output(head);else System.out.println("unsolvable");}
}

参考:http://blog.sina.com.cn/s/blog_6635898a0100p4sx.html

版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://www.cnblogs.com/AndyDai/p/4734124.html

Poj 1077 eight(BFS+全序列Hash解八数码问题)相关推荐

  1. 【基础练习】【BFS+A*】codevs1225八数码难题题解

    题目描写叙述 Description Yours和zero在研究A*启示式算法.拿到一道经典的A*问题,可是他们不会做,请你帮他们. 问题描写叙述 在3×3的棋盘上,摆有八个棋子,每一个棋子上标有1至 ...

  2. 八数码c语言编程深度搜索,广度优先搜索解 八数码, 求意见, 求bug/

    已结贴√ 问题点数:100 回复次数:31 广度优先搜索解 , 求意见, 求bug/ 我得去买票了,.... #include #include #include #define NUM 5 type ...

  3. python 八数码_python 处理八数码 双向BFS 拼图游戏 | 学步园

    我的代码一开始是单向的BFS 然后很慢,5分钟? 参照别人的代码后,改用双向BFS 很快 拼图问题 == 八数码问题 从开始状态start,进行BFS 同时,从结束状态end,进行BFS 如果左图能够 ...

  4. 多种方法求解八数码问题

    AI的实验报告,改了改发上来.希望路过的大牛不吝赐教.非常是纳闷我的ida*怎么还没有双搜快.还有发现基于不在位启示的A*和Ida*都挺慢.尤其是ida* 搜索31步的竟然要十几秒.是我写的代码有问题 ...

  5. 【搜索算法】八数码问题的多种解法

    目录 八数码问题简介 判断是否有解 朴素的 DFS 和 BFS 对于 DFS 和 BFS 剪枝 (去重) 数据结构 map 康托展开 双向BFS A*算法 IDA算法 - 迭代加深的DFS 输出路径的 ...

  6. 八数码问题及A*算法

    一.八数码问题 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同.棋盘上还有一个空格,与空格相邻的棋子可以移到空格中.要求解决的问题是: ...

  7. c语言八数码A星算法代码解析,八数码问题c语言a星算法详细实验报告含代码解析...

    八数码问题c语言a星算法详细实验报告含代码解析 (13页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 一.实验内容和要求 八数码问题:在3 ...

  8. C++解题报告:详解经典搜索难题——八数码问题( 双向BFS A* 求解)

    引言 AC这道八数码问题,你和楼教主就是兄弟了... 题目描述 在一个3*3的九宫格棋盘里,放有8个数码,数码的数字分别是1~8.棋盘中还有一个位置是空着的,用0表示.可以通过在九宫格里平移数码来改变 ...

  9. 八数码 poj 1077 广搜 A* IDA*

    经典的八数码问题,有人说不做此题人生不完整,哈哈. 状态总数是9! = 362880 种,不算太多,可以满足广搜和A*对于空间的需求. 状态可以每次都动态生成,也可以生成一次存储起来,我用的动态生成, ...

最新文章

  1. java jar 版本号_java – 获取JAR文件版本号
  2. LINUX - pthread_detach()与pthread_join()
  3. golang 打印调用堆栈
  4. SD-WAN的出现对MPLS意味着什么?
  5. 基于Mono跨平台移动应用开发框架发布Xamarin 3.0
  6. .net EF监控 MiniProfiler
  7. 方程组的几何解释 [MIT线代第一课pdf下载]
  8. linux c之通过popen和pclose函数创建管道执行shell 运行命令使用总结
  9. ubuntu 修改用户名和计算机名称
  10. spring IoC/DI
  11. Sublime Text 3安装GoSublime
  12. 塞内卡学院实现开源的价值
  13. 心理学家发现脚部动作可表现性格特征
  14. win10+Ubuntu18.04双系统安装后无法进入win10或者无法进入Ubuntu的解决方案
  15. 快速傅里叶变换MATLAB代码实现
  16. 互联网上的UFO教派
  17. 博弈论中SG函数的解释与运用
  18. 模拟扑克牌花色:♠ ♥ ♦ ♣ * 牌号:A 2 3 4 5 6 7 8 9 10 J Q K * 大王、小王 * * 1.生成一副牌 * 2.然后发牌
  19. 腾讯撕开中国NFT的“遮羞布”
  20. webapp封装 苹果app证书

热门文章

  1. Sphinx API文档例子
  2. linux 文本操作
  3. 天融信网闸web界面登录方式_有颜值更有才华,乘风破浪的天融信面板AP接受硬核实测...
  4. 实现裸金属服务器的安全微分段
  5. 权限系统设计学习总结(4)—— SaaS 平台多租户模式下权限设计
  6. 分库分表学习总结(3)——深入理解分布式事务
  7. Netty学习总结(1)——Netty入门介绍
  8. Maven学习总结(3)——使用Maven构建项目
  9. windows 安装docker_Windows下docker安装 postgresql12.0
  10. 电脑无法读取移动硬盘_移动硬盘U盘提示:文件或目录损坏且无法读取如何解决?...