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 ...
随机推荐
- asp.net中使用单例
摘要 有这样一个service,需要运行的asp.net站点上,但要保证这个实例是唯一的.单例用来启用聊天机器人,保证唯一,以免启动多个,造成客户端发送消息的时候,会造成每个机器人都发送消息,app收 ...
- 正则表达式基础---转自 Python正则表达式指南 前边
1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分.正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十 ...
- 【8-21】java学习笔记03
内部类(静态内部类&非静态内部类) 静态外部类成员方法(如main方法)不能直接访问内部类,但是可以通过外部类的方法,在其中创建内部类实例对象,间接使用: 非静态内部类可以直接访问外部类的私有 ...
- 使用/调用 函数的时候, 前面加不加 对象或 this?
这个问题, 其实没有细想: 应该是这样的: (想明白了, 就会少很多困惑, 会对语言的把握 会 更深入更透彻) 任何一门 语言, (如果你自己去设计一门语言...). 都要规定 一些 "关键 ...
- linux的bash 终端操作快捷键
bash中的快捷键操作: 分为两大类, 一是移动, 另一个是删除 一. 移动 1. 按字符移动: ctrl+f, ctrl+b 3. 按行移动: ctrl+a, ctrl+e 二. 删除: 1. 按字 ...
- 密码学初级教程(六)数字签名 Digital Signature
密码学家工具箱中的6个重要的工具: 对称密码 公钥密码 单向散列函数 消息认证码 数字签名 伪随机数生成器 提问: 有了消息认证码为什么还要有数字签名? 因为消息认证码无法防止否认.消息认证码可以识别 ...
- 【C语言入门教程】7.5 枚举
在实际应用中,有的变量只有几种可能取值.如人的性别只有两种可能取值,星期只有七种可能取值.在 C 语言中对这样取值比较特殊的变量可以定义为枚举类型.所谓枚举是指将变量的值一一列举出来,变量只限于列举出 ...
- 淘宝(阿里百川)手机客户端开发日记第十一篇 JSP+Servlet
由于本人从事.net开发已有多年经验,今天由于工作需要,我只能学习下JSP+Servlet,至于java web提供了更好的开发框架MVC,现在由于时间关系,我只好用JSP+Servlet来搭建服务器 ...
- MongoDB的学习和使用(MongoDB GridFS)
MongoDB GridFS GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片.音频.视频等). GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中 ...
- XML类似的解析时,会遇到'XXX' 不是 'NCName' 的有效值的问题
主要原因是:xml中或类xml的文件中有些关键属性的值不符合NCName命名规范,例如我遇到的是流程的bpmn文件中,id的属性值命名的数字开头. NCName 不包含冒号 (:) 的 XML 名称. ...