HDU-1043 Eight八数码 搜索问题(bfs+hash 打表 IDA* 等)
题目链接 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* 等)的更多相关文章
- hdu 1043 Eight (八数码问题)【BFS】+【康拓展开】
<题目链接> 题目大意:给出一个3×3的矩阵(包含1-8数字和一个字母x),经过一些移动格子上的数后得到连续的1-8,最后一格是x,要求最小移动步数. 解题分析:本题用BFS来寻找路径,为 ...
- HDU 1043 Eight 八数码问题 A*算法(经典问题)
HDU 1043 Eight 八数码问题(经典问题) 题意 经典问题,就不再进行解释了. 这里主要是给你一个状态,然后要你求其到达\(1,2,3,4,5,6,7,8,x\)的转移路径. 解题思路 这里 ...
- Hdu 1043 Eight (八数码问题)
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1043 题目描述: 3*3的格子,填有1到8,8个数字,还有一个x,x可以上下左右移动,问最终能否移动 ...
- HUD 1043 Eight 八数码问题 A*算法 1667 The Rotation Game IDA*算法
先是这周是搜索的题,网站:http://acm.hdu.edu.cn/webcontest/contest_show.php?cid=6041 主要内容是BFS,A*,IDA*,还有一道K短路的,.. ...
- hdu1043 经典的八数码问题 逆向bfs打表 + 逆序数
题意: 题意就是八数码,给了一个3 * 3 的矩阵,上面有八个数字,有一个位置是空的,每次空的位置可以和他相邻的数字换位置,给你一些起始状态 ,给了一个最终状态,让你输出怎么变换才能达到目的. 思路: ...
- hdu 1254 推箱子(嵌套搜索,bfs中有dfs)
推箱子 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...
- HDU 1043 Eight 【经典八数码输出路径/BFS/A*/康托展开】
本题有写法好几个写法,但主要思路是BFS: No.1 采用双向宽搜,分别从起始态和结束态进行宽搜,暴力判重.如果只进行单向会超时. No.2 采用hash进行判重,宽搜采用单向就可以AC. No.3 ...
- 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 ...
- HDU1043 Eight(八数码:逆向BFS打表+康托展开)题解
Eight Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Sub ...
随机推荐
- Unity 脚本挂载位置
原则:谁的脚本,挂载到谁身上 1,一般场景中会有个GameController脚本,挂在空物体上. 2,我见很多人脚本习惯挂到Camera上,好吧,不知算不算规范.
- ABBYY简体中文版终身授权半价来袭,真的是5折!
经过了一个春秋,心心念念的双十一终于要来了,一年时间并不长,但这一个月尤其慢!ABBYY官方称为回馈广大用户的支持与厚爱,双十一期间,ABBYY价格感人,诱惑难挡. 说到双十一活动,方式也是五花八门, ...
- LeetCode Golang 4. 寻找两个有序数组的中位数
4. 寻找两个有序数组的中位数 很明显我偷了懒, 没有给出正确的算法,因为官方的解法需要时间仔细看一下... func findMedianSortedArrays(nums1 []int, nums ...
- BZOJ 1030 [JSOI2007]文本生成器 (Trie图+DP)
题目大意:给你一堆字符串,一个串不合法的条件是这些字符串中任意一个是这个串的子串,求合法的串的数量 其实这道题比 [HNOI2008]GT考试 那道题好写一些,但道理是一样的 只不过这道题的答案可以转 ...
- StringUtils 的填充方法
注意:两个参数的用空格填充,三个参数的用后面的参数填充 第一个参数要填充的字符串,第二个是需要的长度,第三个是以什么填充. 左侧填充: leftPad(): StringUtils.leftPad(S ...
- 【【henuacm2016级暑期训练】动态规划专题 O】Robot Rapping Results Report
[链接] 我是链接,点我呀:) [题意] 让你确定一个最小的k 使得1..k这些比赛的结果能够推导出所有人之间的实力大小 [题解] 如果关系越多.那么就越能确定所有人之间的大小关系. (多一点也能唯一 ...
- WinServer-IIS-js无法加载问题
IIS中无法加载JS文件错误 尝试下面的几种解决方法,一起用
- 洛谷 P2243 电路维修
P2243 电路维修 题目背景 Elf 是来自Gliese 星球的少女,由于偶然的原因漂流到了地球上.在她无依无靠的时候,善良的运输队员Mark 和James 收留了她.Elf 很感谢Mark和Jam ...
- JDBC连接mysql时出现的ssl问题
使用MySQL数据库时出现如下错误: WARN: Establishing SSL connection without server's identity verification is not r ...
- sqlplus登录提示:ORA-12162:TNS:net service name is incorrectly specified错误
[root@localhost ~]# su - oracle [oracle@localhost ~]$ sqlplus '/as sysdba' SQL*Plus: Release 11.2.0. ...