ACM/ICPC 之 BFS-广搜进阶-八数码(经典)(POJ1077+HDU1043)
- 八数码问题也称为九宫问题。(本想查查历史,结果发现居然没有词条= =,所谓的历史也就不了了之了)
- 在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。
要求解决的问题是:
给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。- 所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题就是找出从初状态到目标状态所经过的一系列中间状态。
八数码问题一般使用搜索法来解,例如广度优先搜索法、深度优先搜索法、A*算法等。- 用图像来表示就是:
- POJ和HDU上面有两道相同的题目,不过测试数据是HDU比较强
先来看看POJ的解法- POJ上是单组数据即可,所以只需要输入一次,然后一次正向广度优先搜索-BFS(在线)就行了,至于每一个八数码的状态保存方法,用康托展开的方式保存是比较明智的(没有接触过的同学可以问问度娘,本质是使用十进制数保存八进制数),注意不要使用string类型,string的速度很慢,会TLE,直接使用字符串就行了
- Code如下
- //POJ1077-经典BFS-八数码
- //单组数据(HDU1043多组数据较强)
- //正向搜索(用string类型会TLE,所以改用字符串+父结点及方向标记)
- //Time:313Ms Memory:9870K
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- #include<queue>
- using namespace std;
- #define MAX 400000
- #define AIM 46234 //123456780对应的康托Hash值
- bool v[MAX];
- char path[MAX]; //总路径
- int len; //路径长
- /*udlr*/
- char *dir = "udlr"; //正向搜索
- int mov[][] = { { -, }, { , }, { , - }, { , } };
- //八数码状态结构体
- struct Node{
- int s[];
- int loc; //空位
- int status; //Hash值-排列值
- int fa; //记录父状态
- char d; //到此状态的移动方向
- }n[MAX];
- int fac[] = { , , , , , , , , , };
- //对序列ncur.s[9]康托展开-返回Hash值
- int cantor(int s[])
- {
- int sum = ;
- for (int i = ; i < ; i++)
- {
- int num = ; //逆序数计数器
- for (int j = i + ; j < ; j++)
- if (s[j] < s[i])
- num++;
- sum += num*fac[ - i - ];
- }
- return sum + ;
- }
- /*反向记录路径*/
- void count_path(Node end)
- {
- int f = end.fa;
- len = ;
- path[len++] = end.d;
- while (f)
- {
- path[len++] = n[f].d;
- f = n[f].fa;
- }
- }
- bool BFS()
- {
- memset(v, , sizeof(v));
- Node next; //下一临时状态
- int head = , tail = ;
- n[].status = cantor(n[].s);
- v[n[].status] = true;
- while (head <= tail) //模拟队列
- {
- if (n[head].status == AIM) //达到AIM
- {
- count_path(n[head]);
- return true;
- }
- //计算二维坐标
- int x = n[head].loc / ;
- int y = n[head].loc % ;
- for (int i = ; i < ; i++)
- {
- int tx = x + mov[i][];
- int ty = y + mov[i][];
- if (tx < || tx> || ty < || ty>)continue;
- //新状态更新
- next = n[head];
- next.loc = tx * + ty; //计算新空位
- next.s[n[head].loc] = next.s[next.loc]; //原空位替换
- next.s[next.loc] = ; //新空位
- next.fa = head;
- next.d = dir[i];
- next.status = cantor(next.s);
- //判重并入队
- if (!v[next.status])
- {
- v[next.status] = true;
- if (next.status == AIM)
- {
- count_path(next);
- return true;
- }
- n[++tail] = next;
- }
- }
- head++;
- }
- return false;
- }
- int main()
- {
- /*input*/
- char ch[];
- for (int i = ; i < ; i++)
- {
- scanf("%s",ch);
- if (!strcmp(ch,"x"))
- {
- n[].s[i] = ;
- n[].loc = i;
- }
- else n[].s[i] = ch[] - '';
- }
- /*output*/
- if (BFS())
- { //反向输出路径
- for (int i = len - ; i>=; i--)
- printf("%c", path[i]);
- printf("\n");
- }
- else
- printf("unsolvable\n");
- return ;
- }
- HDU解法如下:
HDU上是多组数据,因此 在线BFS 的方法会重复计算很多次,今天重点说说广度优先搜索,因此我们用 离线BFS 方法(也就是打表的思想)计算出所有路径并保存,
之后输入状态只需要计算出其康托逆展开的Hash值,就可以直接输出对应的路径了
- //HDU1043-经典BFS-八数码
- //多组数据-需要计算全部路径后直接输出(POJ1077数据较弱)
- //反向搜索+打表(离线)
- //Time:109Ms Memory:25412K
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- #include<queue>
- using namespace std;
- #define MAX 400000
- #define AIM 46234 //123456780对应的康托Hash值
- bool v[MAX];
- char path[MAX][]; //总路径
- int len; //路径长
- /*udlr*/
- char *dir = "durl"; //反向搜索
- int mov[][] = { { -, }, { , }, { , - }, { , } };
- //八数码状态结构体
- struct Node{
- int s[];
- int loc; //空位
- int status; //Hash值-排列值
- int fa; //记录父状态
- char d; //到此状态的移动方向
- }n[MAX];
- int fac[] = { , , , , , , , , , };
- //康托逆展开-返回Hash值
- int Inverse_cantor(int s[])
- {
- int sum = ;
- for (int i = ; i < ; i++)
- {
- int num = ; //逆序数计数器
- for (int j = i + ; j < ; j++)
- if (s[j] < s[i])
- num++;
- sum += num*fac[ - i - ];
- }
- return sum + ;
- }
- /*反向记录路径*/
- void count_path(Node end)
- {
- int status = end.status;
- int f = end.fa;
- len = ;
- path[status][len++] = end.d;
- while (f)
- {
- path[status][len++] = n[f].d;//方向记录
- f = n[f].fa; //查找父结点
- }
- }
- void BFS()
- {
- memset(v, , sizeof(v));
- Node next; //下一临时状态
- int head = , tail = ;
- /*目标状态*/
- for (int i = ; i < ; i++)
- n[].s[i] = i + ;
- n[].s[] = ;
- n[].loc = ;
- n[].status = AIM;
- v[AIM] = true;
- while (head <= tail) //模拟队列
- {
- //计算二维坐标
- int x = n[head].loc / ;
- int y = n[head].loc % ;
- for (int i = ; i < ; i++) //遍历四方向
- {
- int tx = x + mov[i][];
- int ty = y + mov[i][];
- if (tx < || tx> || ty < || ty>)continue;
- //新状态更新
- next = n[head];
- next.loc = tx * + ty; //计算新空位
- next.s[n[head].loc] = next.s[next.loc]; //原空位替换
- next.s[next.loc] = ; //新空位
- next.fa = head;
- next.d = dir[i];
- next.status = Inverse_cantor(next.s);
- //判重并入队
- if (!v[next.status])
- {
- v[next.status] = true;
- count_path(next);
- n[++tail] = next;
- }
- }
- head++;
- }
- }
- int main()
- {
- /* BFS-打表 */
- BFS();
- /*input*/
- char ch[];
- Node cur;
- while (scanf("%s", ch) != EOF)
- {
- if (!strcmp(ch, "x"))
- cur.s[] = , cur.loc = ;
- else cur.s[] = ch[] - '';
- for (int i = ; i < ; i++)
- {
- scanf("%s", ch);
- if (!strcmp(ch, "x"))
- cur.s[i] = , cur.loc = i;
- else cur.s[i] = ch[] - '';
- }
- cur.status = Inverse_cantor(cur.s);
- /*output*/
- if (v[cur.status])
- printf("%s\n", path[cur.status]);
- else
- printf("unsolvable\n");
- }
- return ;
- }
ACM/ICPC 之 BFS-广搜进阶-八数码(经典)(POJ1077+HDU1043)的更多相关文章
- hdu 1195:Open the Lock(暴力BFS广搜)
Open the Lock Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...
- BFS广搜题目(转载)
BFS广搜题目有时间一个个做下来 2009-12-29 15:09 1574人阅读 评论(1) 收藏 举报 图形graphc优化存储游戏 有时间要去做做这些题目,所以从他人空间copy过来了,谢谢那位 ...
- ACM/ICPC 之 BFS(离线)+康拓展开(TSH OJ-玩具(Toy))
祝大家新年快乐,相信在新的一年里一定有我们自己的梦! 这是一个简化的魔板问题,只需输出步骤即可. 玩具(Toy) 描述 ZC神最擅长逻辑推理,一日,他给大家讲述起自己儿时的数字玩具. 该玩具酷似魔方, ...
- hdu 1242:Rescue(BFS广搜 + 优先队列)
Rescue Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/32768K (Java/Other) Total Submis ...
- hdu 1026:Ignatius and the Princess I(优先队列 + bfs广搜。ps:广搜AC,深搜超时,求助攻!)
Ignatius and the Princess I Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (J ...
- hdu 1180:诡异的楼梯(BFS广搜)
诡异的楼梯 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Total Subm ...
- 【双向广搜+逆序数优化】【HDU1043】【八数码】
HDU上的八数码 数据强的一B 首先:双向广搜 先处理正向搜索,再处理反向搜索,直至中途相遇 visit 和 队列都是独立的. 可以用一个过程来完成这2个操作,减少代码量.(一般还要个深度数组) 优化 ...
- hdu 2717:Catch That Cow(bfs广搜,经典题,一维数组搜索)
Catch That Cow Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- hdu 2612:Find a way(经典BFS广搜题)
Find a way Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
随机推荐
- JSF页面中使用js函数回调后台bean方法并获取返回值的方法
由于primefaces在国内使用的并不是太多,因此,国内对jsf做系统.详细的介绍的资料很少,即使有一些资料,也仅仅是对国外资料的简单翻译或者是仅仅讲表面现象(皮毛而已),它们的语句甚至还是错误的, ...
- java设计模式设计模式
JAVA设计模式之单例模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一 ...
- VMnet1和V8
vmware默认使用了两个虚拟网卡: vmnet1 v1 vmnet8 v8 vmnet1是host-only,也就是说,选择用vmnet1的话就相当于VMware给你提供了一个虚拟交换机,仅将虚拟机 ...
- hdu4888 Redraw Beautiful Drawings 最大流+判环
hdu4888 Redraw Beautiful Drawings Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 65536/6553 ...
- HTTP报文详解
二.HTTP请求首部字段 1 Accept 2 Accept-Charset 3 Accept-Encoding 4 Accept-Language 5 Authorization 6
- Excel 使用宏批量修改单元格内指定文字为红字
-> step 1:新建宏,进入编辑,使用如下代码: Sub Ss()Dim c As RangeFor Each c In ActiveSheet.UsedRange i = 1 While ...
- 【codevs1380】没有上司的舞会
题目描述 Ural大学有N个职员,编号为1~N.他们有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.每个职员有一个快乐指数.现在有个周年庆宴会,要求与会职员的快乐指数 ...
- 基于Windows10安装Ubuntu双系统
步骤: 1.从Ubuntu的官网上下载Ubuntu的iSO安装包. http://www.ubuntu.com/download/ 我安装的版本是Ubuntu 14.04.3 LTS 64位版本 2. ...
- ajax异步提交数据动态更改select选项
<!DOCTYPE html> <html> <head> <title></title> <script src="../ ...
- C++Socket编程总结 [转]
使用socket写代码主要是要看自己的需求是什么. 如果通信时,内容很重要就要使TCP方式. 如果用户数太多,可能就要使用UDP方式了. 在TCP模式下,最简单的方式就是这样的,使阻塞方式: 服务端: ...