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 ...
随机推荐
- GFM(GitHub Flavored Markdown)与标准Markdown的语法区别
没有就自己造 其实这篇文16年底的时候就写好的,只不过当时是记在自己的笔记上(没错,我笔记大多记在本地,比发表的这寥寥几篇那是多的多的多了
- 模板 NTT 快速数论变换
NTT裸模板,没什么好解释的 这种高深算法其实也没那么必要知道原理 #include <cstdio> #include <cstring> #include <algo ...
- freeswitch 注册错误408 - Request Timeout
1.网络不通(可能虚拟机没打开网络) 2.防火墙没有过滤端口号(关闭防火墙) 3.IP地址错误
- debian 9 安装无线网卡
#添加源 echo "deb http://httpredir.debian.org/debian/ stretch main contrib non-free" >> ...
- 正式版的Linux Kernel 5.1来了,非LTS
大神Linus Torvalds于今天发布了Linux Kernel 5.1内核正式版,在对现有功能进行改进的同时还带来了很多重要的改进.本次版本更新历时一个半月,不过值得注意的是它并非是长期支持版本 ...
- TI低功耗蓝牙(BLE)介绍
TI低功耗蓝牙(BLE)介绍 本文档翻译和修改自参考资料:CC2540Bluetooth Low Energy Software Developer’s Guide (Rev. B),部分图片直接引用 ...
- JavaScript中的基础测试题
Java ...
- Linux Mint 17.1 安装全配置
Linux Mint 17.1 安装全配置 I. 前言 由于自己的本子出现了一些故障需要重新安装系统,就上网看看今年4,5月份发布的一些新的发行版来试试.原先电脑上安装的是opensuse13.2, ...
- Docker可视化管理工具对比(DockerUI、Shipyard、Rancher、Portainer)
1.前言 谈及docker,避免不了需要熟练的记住好多命令及其用法,对于熟悉shell.技术开发人员而言,还是可以接受的,熟练之后,命令行毕竟是很方便的,便于操作及脚本化.但对于命令行过敏.非技术人员 ...
- dubbo 部分 配置的关系-dubbo github 官方案例
1.dubbo 有一个 dubbo.properties 作为默认配置 默认配置可以在不添加新的配置的前提下使用dubbo dubbo.properties 的内容(来自 https://github ...