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 ...
随机推荐
- NGUI 按钮点击事件的两种绑定形式
面板属性栏绑定 写一个脚本,定义一个Public的方法 Notify中选择物体时,选中自己 然后就可以选择通知到写的那个脚本的里边的public方法 代码绑定 创建一个代码文件,挂载到按钮对象上 代码 ...
- vue2.0变化(转载)
原文链接:https://www.cnblogs.com/itbainianmei/p/6062249.html 1.每个组件模板template,不再支持片段代码 之前: <template& ...
- BZOJ 2150 部落战争 (二分图匹配)
题目大意:给你一个n*m的棋盘,有一些坏点不能走,你有很多军队,每支军队可以像象棋里的马一样移动,不过马是1*2移动的,而军队是r*c移动的,军队只能从上往下移动,如果一个点已经被一直军队经过,那么其 ...
- [spoj] HIGH - Highways (生成树计数)
传送门 输入格式: 第一行一个整数T,表示测试数据的个数 每个测试数据第一行给出 n,m 分别表示点数与边数 接下来 m 行,每行给出两个数 a,b ,表示 a,b 之间有一条无向边 输出格式: 每个 ...
- 2019-03-18 使用Request POST获取CNABS网站上JSON格式的表格数据,并解析出来用pymssql写到SQL Server中
import requests import pymssql url = 'https://v1.cn-abs.com/ajax/ChartMarketHandler.ashx' headers = ...
- poj 3311 Hie with the Pie (状压dp) (Tsp问题)
这道题就是Tsp问题,稍微加了些改变 注意以下问题 (1)每个点可以经过多次,这里就可以用弗洛伊德初始化最短距离 (2)在循环中集合可以用S表示更清晰一些 (3)第一维为状态,第二维为在哪个点,不要写 ...
- 洛谷 P2243 电路维修
P2243 电路维修 题目背景 Elf 是来自Gliese 星球的少女,由于偶然的原因漂流到了地球上.在她无依无靠的时候,善良的运输队员Mark 和James 收留了她.Elf 很感谢Mark和Jam ...
- scrapy研究探索(二)——爬w3school.com.cn
下午被一个问题困扰了好一阵.终于使用还有一种方式解决. 開始教程二.关于Scrapy安装.介绍等请移步至教程(一)(http://blog.csdn.net/u012150179/article/de ...
- Android recycleView的研究和探讨
RecyclerViewLibrary A RecyclerView libirary ,has some support, like headerAdapter/TreeAdapter,and Pu ...
- 试用友盟SDK实现Android分享微信朋友圈
社会化分享是眼下必学且火热的功能.之前有写第三方登录,那仅仅是社会化分享的一部分.今天来玩玩分享微信朋友圈. 为了方便操作,还是依照步骤写. 一,注冊 注冊应用已经在这里具体说明过了,这里就不多提了. ...