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 ...
随机推荐
- Event-driven programming-main loop
In computer programming, event-driven programming is a programming paradigm in which the flow of the ...
- 我用windows live Writer 写个日志试试看
我用windows live Writer 写个日志试试看. 哈哈 播放幻灯片 全部下载
- Kattis - How Many Digits?
How Many Digits? Often times it is sufficient to know the rough size of a number, rather than its ex ...
- java开发移动端之spring的restful风格定义
https://www.ibm.com/developerworks/cn/web/wa-spring3webserv/index.html
- Day 03 知识点[python程序运行的方式、变量、注释、内存管理、数据类型]
执行Python程序的两种方式 第一种:交互式,在cmd中运行 优点:调试程序方便,直接给出结果 缺点:无法保存,关掉cmd窗口数据就消失 第二种:命令行式通过cmd中输入Python3文本 优点:数 ...
- There are multiple modules with names that only differ in casing.
client?4c0e:153 ./src/components/Paginate.vue There are multiple modules with names that only differ ...
- Django REST Framework 认证 - 权限 - 限制
一. 认证 (你是谁?) REST framework 提供了一些开箱即用的身份验证方案,并且还允许你实现自定义方案. 自定义Token认证 第一步 : 建表>>>> 定义一个 ...
- NOIP2018提高组省一冲奖班模测训练(三)
NOIP2018提高组省一冲奖班模测训练(三) 自己按照noip的方式考,只在最后一两分钟交了一次 第一题过了,对拍拍到尾. 第二题不会.考试时往组合计数的方向想,推公式,推了一个多小时,大脑爆炸,还 ...
- [luogu] P2569 [SCOI2010]股票交易 (单调队列优化)
P2569 [SCOI2010]股票交易 题目描述 最近 \(\text{lxhgww}\) 又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律. 通过一段时间的观察,\(\te ...
- spring boot约定优于配置的这种做法在如今越来越流行了
约定优于配置的这种做法在如今越来越流行了,它的特点是简单.快速.便捷.但是这是建立在程序员熟悉这些约定的前提上.而 Spring 拥有一个庞大的生态体系,刚开始转到 Spring Boot 完全舍弃 ...