题目链接 https://vjudge.net/problem/HDU-1043

经典的八数码问题,学过算法的老哥都会拿它练搜索

题意:

给出每行一组的数据,每组数据代表3*3的八数码表,要求程序复原为初始状态

思路:

参加网站比赛时拿到此题目,因为之前写过八数码问题,心中暗喜,于是写出一套暴力bfs+hash,结果TLE呵呵

思路一:bfs+hash(TLE)

 #include <cstdio>
#include <cstring>
#include <queue>
#include <set>
using namespace std;
const int StMax=, HashMax=;
struct State{
char map[][];
int dis, fx, x, y, id, fa;
}start, st[StMax];
int head[HashMax], mynext[StMax], dir[][]={{,},{-,},{,},{,-}};
char ch[]={'r', 'l', 'd', 'u'};
int myhash(State &a){
a.id=;
for (int y=; y<; y++)
for (int x=; x<; x++)
a.id=a.id*+((a.map[y][x]=='x')?'':a.map[y][x])-'';
return a.id%HashMax;
}
int insert(int rear){
int h=myhash(st[rear]), u=head[h];
while(u){
if (st[rear].id==st[u].id) return ;
u=mynext[u];
}
mynext[rear]=head[h]; head[h]=rear;
return ;
}
void output(int u){
if (u==) printf("unsolvable");
else if (u==) return;
else{
output(st[u].fa);
printf("%c", ch[st[u].fx]);
}
} int bfs(void){
st[]=start; insert();
if (start.id==) return ;
int front=, rear=;//2,1 for hash
while (front<rear){
State &s=st[front];
for (int i=; i<; i++){
int nx=s.x+dir[i][], ny=s.y+dir[i][]; if (nx< || nx>= || ny< || ny>=) continue;
State &t=st[rear]; memcpy(&t, &s, sizeof(s));
t.map[s.y][s.x]=s.map[ny][nx];
t.map[ny][nx]='x';
if (!insert(rear)) continue;
t.x=nx; t.y=ny; t.fx=i; t.dis++; t.fa=front; if (t.id==) return rear;
rear++;
}front++;
}
return ;
}
int input(void){
char a[]; int p=, re;
if ((re=scanf("%[^\n]\n", a))!=) return ;
for (int y=; y<; y++)
for (int x=; x<; x++){
while(a[p]==' ') p++;
if ((start.map[y][x]=a[p])=='x') {start.x=x; start.y=y;}
p++;
}
start.dis=;
return ;
} int main(void){
while (input()){
memset(head, , sizeof(head));
memset(mynext, , sizeof(mynext));
output(bfs()); printf("\n");
} return ;
}

看来hdu的数据比较强,比较多,考虑到八数码问题状态数不是非常大(<9!=362880<10^6)

(注:参考紫书 一般情况状态总数小于10^6在可接受范围)
于是考虑bfs的预处理打表,在此期间了解到康托展开用以编码全排列

思路二:bfs打表+cantor(AC)

中间三个数据分别是Time(ms) Mem(MB) Length

 #include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
typedef int State[];
const int STMAX=;
int fact[]={,,,,,,,,,}, dir[][]={,-,-,,,,,};
int st[STMAX][], vis[STMAX], myprev[STMAX], fx[STMAX], goal=, stcode[STMAX];
char toch[]={'d','r','u','l'};//反方向
int encode(int map[], int n){
int code=;
for (int i=; i<n; i++){
int cnt=;
for (int j=i+; j<n; j++)
if (map[i]>map[j]) cnt++;
code+=cnt*fact[n--i];
}return code;
} int input(void){
char ch;
for (int i=; i<; i++){
do{if (scanf("%c", &ch)!=) return ;}while(ch==' '||ch=='\n');
if (ch=='x'||ch=='X') ch='';
st[][i]=ch-'';
}
return ;
} int check(void){
int sum=;
for (int i=; i<; i++){
if (st[][i]==) continue;
for (int j=i+; j<; j++){
if (st[][j]==) continue;
if (st[][i]>st[][j]) sum++;
}
}
return sum;
} void show(vector<char> &path, int code){
if (code==goal) return;
else{
show(path, myprev[code]);
path.push_back(toch[fx[code]]);
}
} void pre(void){
memset(vis, , sizeof(vis));
memset(myprev, , sizeof(myprev));
State s={,,,,,,,,}; memcpy(st[], &s, sizeof(s));
vis[stcode[]=encode(st[], )]=;
int front=, rear=;
while (front<rear){
State &a=st[front]; int z=; while (a[z]) z++;
for (int i=; i<; i++){
int nx=z%+dir[i][], ny=z/+dir[i][];
if (nx< || nx> || ny< || ny>) continue;
State &b=st[rear]; memcpy(&b, &a, sizeof(a));
b[nx+ny*]=; b[z]=a[nx+ny*]; int code=encode(b, );
if (vis[code]) continue;
fx[code]=i; myprev[code]=stcode[front];
stcode[rear]=code; vis[code]=; rear++;
}front++;
}
} int main(void){
pre();
while (input()){
vector<char> path;
int code=encode(st[], );
if (!vis[code]) printf("unsolvable\n");
else {
show(path, code);
for (int i=path.size()-; i>=; i--)
printf("%c", path[i]);
printf("\n");
}
} return ;
}

解题到此结束,但在此期间想到过新学的IDA*,按结果来说也是不错的

思路三:IDA*(AC)


(没错,我特地重新上传了一次,因为之前的代码有不少啰嗦的地方)

我觉得此题用作IDA*的入门题目非常合适,dfs()中排除上次操作的反方向(prevDir)是一个很实用的小技巧,排除了许多分支

 #include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
using namespace std;
typedef int State[];
State st, goal={,,,,,,,,};
int maxd;
int isdir[]={,,,}, orix[]={,,,,,,,,}, oriy[]={,,,,,,,,}, dir[][]={,-,-,,,,,};
char toch[]={'u', 'l', 'd', 'r'};
int input(void){
char ch;
for (int i=; i<; i++){
do{if(scanf("%c", &ch)!=) return ;}while (ch==' '||ch=='\n');
if (ch=='x') ch='';
st[i]=ch-'';
}
return ;
} int check(void){
int sum=;
for (int i=; i<; i++){
if (st[i]==) continue;
for (int j=i+; j<; j++){
if (st[j]==) continue;
if (st[i]>st[j]) sum++;
}
}
return sum;
}
inline int calc(State &a){
int sum=;
for (int i=; i<; i++)
sum+=abs(i%-orix[st[i]])+abs(i/-oriy[st[i]]);
return sum;
} int dfs(State &a, vector<char> &path, int z, int prevdir, int d){
int h=calc(a);
if (h==) return ;
if (maxd==d) return ; if (h>*(maxd-d)) return ;
for (int i=; i<; i++){
if (prevdir!=- && isdir[prevdir]==i) continue;//great effect
int nx=z%+dir[i][], ny=z/+dir[i][];
if (nx< || nx> || ny< || ny>) continue;
a[z]=a[nx+ny*]; a[nx+ny*]=; path.push_back(toch[i]);
if (dfs(a, path, nx+ny*, i, d+)) return ;
a[nx+ny*]=a[z]; a[z]=; path.pop_back();
}return ;
} int main(void){
while (input()){
if (check()%) {printf("unsolvable\n"); continue;}
int z=; while(st[z]) z++;
for (maxd=; ; maxd++){
vector<char> path;
if (dfs(st, path, z, -, )){
for (int i=; i<path.size(); i++) printf("%c", path[i]);
printf("\n");
break;
}
}
}
return ;
}

其他思路:

双向BFS:

若需要路径,则一定需判断节点是否由另一队列走过,并链接两队列中的路径(考虑cantor)
A*+cantor:

使用priority_queue(优先队列),启发函数类似IDA*

(实际情况下我比较喜欢IDA*,因为它比较短,也好找错。。。)

HDU-1043 Eight八数码 搜索问题(bfs+hash 打表 IDA* 等)的更多相关文章

  1. hdu 1043 Eight (八数码问题)【BFS】+【康拓展开】

    <题目链接> 题目大意:给出一个3×3的矩阵(包含1-8数字和一个字母x),经过一些移动格子上的数后得到连续的1-8,最后一格是x,要求最小移动步数. 解题分析:本题用BFS来寻找路径,为 ...

  2. HDU 1043 Eight 八数码问题 A*算法(经典问题)

    HDU 1043 Eight 八数码问题(经典问题) 题意 经典问题,就不再进行解释了. 这里主要是给你一个状态,然后要你求其到达\(1,2,3,4,5,6,7,8,x\)的转移路径. 解题思路 这里 ...

  3. Hdu 1043 Eight (八数码问题)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1043 题目描述: 3*3的格子,填有1到8,8个数字,还有一个x,x可以上下左右移动,问最终能否移动 ...

  4. HUD 1043 Eight 八数码问题 A*算法 1667 The Rotation Game IDA*算法

    先是这周是搜索的题,网站:http://acm.hdu.edu.cn/webcontest/contest_show.php?cid=6041 主要内容是BFS,A*,IDA*,还有一道K短路的,.. ...

  5. hdu1043 经典的八数码问题 逆向bfs打表 + 逆序数

    题意: 题意就是八数码,给了一个3 * 3 的矩阵,上面有八个数字,有一个位置是空的,每次空的位置可以和他相邻的数字换位置,给你一些起始状态 ,给了一个最终状态,让你输出怎么变换才能达到目的. 思路: ...

  6. hdu 1254 推箱子(嵌套搜索,bfs中有dfs)

    推箱子 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...

  7. HDU 1043 Eight 【经典八数码输出路径/BFS/A*/康托展开】

    本题有写法好几个写法,但主要思路是BFS: No.1 采用双向宽搜,分别从起始态和结束态进行宽搜,暴力判重.如果只进行单向会超时. No.2 采用hash进行判重,宽搜采用单向就可以AC. No.3 ...

  8. Eight (HDU - 1043|POJ - 1077)(A* | 双向bfs+康拓展开)

    The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've see ...

  9. HDU1043 Eight(八数码:逆向BFS打表+康托展开)题解

    Eight Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Sub ...

随机推荐

  1. Python IDE和编辑器

    1.什么是IDE? IDE也就是集成开发环境,较常用的有PyCharm 2.编辑器 (1)Sublime Text Sublime Text 具有漂亮的用户界面和强大的功能,例如代码缩略图,Pytho ...

  2. github删除项目or仓库

    1. 登录 github (要注册账号) 2. 登录后点击右上侧头像,选择 Your profile . 3. 选择Repositories,可以查看已有的库,选择要删除的库进入. 4. 选择Sett ...

  3. 【安装配置Redis】

    目录 安装 配置 Redis官网:https://redis.io Redis是完全开源免费的,遵守BSD协议. Redis是一个高性能的key-value数据库. @ *** Redis具有以下特点 ...

  4. Java基础学习总结(33)——Java8 十大新特性详解

    Java8 十大新特性详解 本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API ...

  5. ZJU 2676 Network Wars

    Network Wars Time Limit: 5000ms Memory Limit: 32768KB This problem will be judged on ZJU. Original I ...

  6. 洛谷1417 烹调方案 dp 贪心

    洛谷 1417 dp 传送门 挺有趣的一道dp题目,看上去接近于0/1背包,但是考虑到取每个点时间不同会对最后结果产生影响,因此需要进行预处理 对于物品x和物品y,当时间为p时,先加x后加y的收益为 ...

  7. Windows环境下教你用Eclipse ADT 插件生成.h/.so文件,Java下调用JNI,轻松学习JNI

    准备工作:Eclipse ADT IDE 开发工具,NDK .Java 环境,博主的配置是:Windows x86 , ADT Build: v22.3.0-887826 , JAVA 1.7, ND ...

  8. leetCode 72.Edit Distance (编辑距离) 解题思路和方法

    Edit Distance Given two words word1 and word2, find the minimum number of steps required to convert  ...

  9. easyui编辑器(kindeditor-4.1.10)

    //1  重写kindedit    -建一个js文件 easyui_kindeditor.js (function ($, K) {     if (!K)         throw " ...

  10. C#高级编程五十八天----并行集合

    并行集合 对于并行任务,与其相关紧密的就是对一些共享资源,数据结构的并行訪问.常常要做的就是对一些队列进行加锁-解锁,然后运行类似插入,删除等等相互排斥操作. .NET4提供了一些封装好的支持并行操作 ...