hdu 3567 Eight II 八数码 双向BFS
2014-07-27 14:04
357 查看
题意:经典的八数码题目。一个3*3矩阵,由8个数字以及一个X组成,X每次可以与它相邻的数字交换,给定一个开始序列以及一个目标序列,问最少需要多少次操作能从开始序列到达目标序列,若存在多个最短路径,则输出字典序最小的一条。
题解:
我死在了路径输出上面,一开始没看到字典序,双向BFS轻轻松松,后来发现了,
,反方向bfs没办法直接保证字典序。。
这倒题直接BFS应该会爆掉(没试过,不过看情况是这样);后来想到了双向BFS和A*算法,A*算法没怎么学,先用双向BFS过掉它。
双向BFS就是从开始和结果同时BFS,判相遇(详细请百度)。这道题,需要做哈希表的空间压缩,因为直接哈希肯定要爆掉的,将X看做0,这么就可以用康托展开来压缩哈希表的空间了(原理就是讲这几个数压缩成全排列中的第个数,【康托展开-维基百科】可以找到)。压缩后总的状态数只有360000个左右;
之后双向BFS,BFS是正常的BFS,只是路径处理要注意了。前BFS的路径可以通过小方向先搜索来保证字典序,后BFS就不行了,(被wa了几次之后才发现要字典序,汗。。,之后看查双向BFS的路经输出,看到后BFS只要大方向先输出就可以了,又再次被坑。。),需要的方法就是每个位置(也就是哈希值)记录下这序列到目标序列的字典序最小路径,当bfs访问已访问位置时,判断距离,距离相等的情况就要保留字典序最小的路径,并加入队列。一开始想到的是用字符串来保存路径,被TLE了。。字符串操作太多,使得跑200组要3S多,之后就想到了二进制的保存方式(这里是四进制,四个方向嘛),那么用一个整数就能保存一条路径,之后判相遇后逆运算即可。
注意:相遇后还是要把相同距离的都判一遍,一开始我就遗漏了。。
提示:可以先做hdu 1043 Eight,难度更简单的一道。
代码:
题解:
我死在了路径输出上面,一开始没看到字典序,双向BFS轻轻松松,后来发现了,
,反方向bfs没办法直接保证字典序。。
这倒题直接BFS应该会爆掉(没试过,不过看情况是这样);后来想到了双向BFS和A*算法,A*算法没怎么学,先用双向BFS过掉它。
双向BFS就是从开始和结果同时BFS,判相遇(详细请百度)。这道题,需要做哈希表的空间压缩,因为直接哈希肯定要爆掉的,将X看做0,这么就可以用康托展开来压缩哈希表的空间了(原理就是讲这几个数压缩成全排列中的第个数,【康托展开-维基百科】可以找到)。压缩后总的状态数只有360000个左右;
之后双向BFS,BFS是正常的BFS,只是路径处理要注意了。前BFS的路径可以通过小方向先搜索来保证字典序,后BFS就不行了,(被wa了几次之后才发现要字典序,汗。。,之后看查双向BFS的路经输出,看到后BFS只要大方向先输出就可以了,又再次被坑。。),需要的方法就是每个位置(也就是哈希值)记录下这序列到目标序列的字典序最小路径,当bfs访问已访问位置时,判断距离,距离相等的情况就要保留字典序最小的路径,并加入队列。一开始想到的是用字符串来保存路径,被TLE了。。字符串操作太多,使得跑200组要3S多,之后就想到了二进制的保存方式(这里是四进制,四个方向嘛),那么用一个整数就能保存一条路径,之后判相遇后逆运算即可。
注意:相遇后还是要把相同距离的都判一遍,一开始我就遗漏了。。
提示:可以先做hdu 1043 Eight,难度更简单的一道。
代码:
#include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> #include <iostream> #include <queue> #include <map> #include <vector> using namespace std; #define LL __int64 const int maxn=4e5+10; const int INF=1e8; int ha[9]={1,1,2,6,24,120,720,5040,40320}; int dir[4][2]={{1,0},{0,-1},{0,1},{-1,0}}; char d[10]={"dlru"}; LL dd[2][4]={{0,1,2,3},{3,2,1,0}}; int vis[2][maxn],t,Min_ans;//vis记录最短到底目标的时间 char ans[maxn],get[maxn]; LL c[2][maxn];//c[i][j]表示i状态下,j哈希值时的最小路径 LL mm[30]; //mm[i]表示4^i struct node{ int f[3][3]; int x,y; int g; int flag;//0表示前BFS,1表示后BFS LL path; int hash_num; }; int get_hash(node e)//康托展开,压缩空间。 { int a[9],i,j,ii,jj,k=0,ans=0; for(i=0;i<3;i++) { for(j=0;j<3;j++) a[k++]=e.f[i][j]; } for(i=0;i<9;i++) { k=0; for(j=0;j<i;j++) if(a[j]>a[i])k++; ans+=ha[i]*k; } return ans; } string get_str(LL c,int flag,int kk)//从数字转换成路径 { int str[100]; int i,j,k=0; for(i=0;i<vis[flag][kk];i++) { str[k++]=c%4; c=c/4; } string s=""; for(i=k-1;i>=0;i--) s+=d[str[i]]; return s; } void bfs(node e,node ee)//双向bfs { memset(vis,-1,sizeof(vis)); int i,j,k,xx,yy,dis[2]; dis[0]=dis[1]=0; node a,b; e.hash_num=get_hash(e); e.g=0,e.flag=0; e.path=0; ee.hash_num=get_hash(ee); ee.g=0,ee.flag=1; ee.path=0; vis[0][e.hash_num]=0; vis[1][ee.hash_num]=0; if(e.hash_num==ee.hash_num){printf("0\n\n");return;} queue<node>q; q.push(e); q.push(ee); Min_ans=INF; LL str; string res; while(!q.empty()) { e=q.front(); q.pop(); for(i=0;i<4;i++) { a=e; a.x=e.x+dir[i][0]; a.y=e.y+dir[i][1]; if(a.x<0||a.y<0||a.x>=3||a.y>=3)continue; swap(a.f[e.x][e.y],a.f[a.x][a.y]); k=get_hash(a); if(vis[e.flag][k]!=-1) { if(e.g+1>vis[e.flag][k])continue; else { if(e.flag)str=dd[e.flag][i]*mm[e.g]+e.path; else str=e.path*4+dd[e.flag][i]; if(c[e.flag][k]>str) c[e.flag][k]=str; } } else { vis[e.flag][k]=e.g+1; if(e.flag)c[e.flag][k]=dd[e.flag][i]*mm[e.g]+e.path; else c[e.flag][k]=e.path*4+dd[e.flag][i]; } a.hash_num=k; a.g++; a.path=c[e.flag][k]; if(vis[e.flag^1][k]!=-1) { //cout<<vis[0][k]+vis[1][k]<<endl; //cout<<c[0][k]<<" "<<c[1][k]<<endl; string s=get_str(c[0][k],0,k)+get_str(c[1][k],1,k); //cout<<s<<endl; t=s.length(); if(t>Min_ans) { cout<<Min_ans<<endl; cout<<res<<endl; return; } if(t<Min_ans) { Min_ans=t; res=s; } else { if(res.compare(s)>0)res=s; } } q.push(a); } } } void init() { int i,j,k; mm[0]=1; for(i=1;i<=30;i++) mm[i]=mm[i-1]*4; } int main() { //freopen("C:\\Documents and Settings\\All Users\\桌面\\in.txt","r",stdin); //freopen("C:\\Documents and Settings\\All Users\\桌面\\out1.txt","w",stdout); init(); char a[30],b[30]; int T,tt=0; scanf("%d",&T); while(T--) { int i,j,k,n; node e,pp; scanf("%s",a); scanf("%s",b); n=strlen(a); for(i=0;i<n;i++) { if(a[i]=='X'){e.f[i/3][i%3]=0;e.x=i/3;e.y=i%3;} else e.f[i/3][i%3]=a[i]-'0'; if(b[i]=='X'){pp.f[i/3][i%3]=0;pp.x=i/3;pp.y=i%3;} else pp.f[i/3][i%3]=b[i]-'0'; } printf("Case %d: ",++tt); bfs(e,pp); } return 0; } /* 100 738165X42 51674X328 uurdldruruldlurddlurdru ruuldrdluurdruldlurddru */ /* 数据生成: #include <iostream> #include <queue> #include <cstdio> #include <cstring> #include <algorithm> #include <ctime> #include <vector> #include <cmath> #include <cstdlib> using namespace std; const int maxn=1e3+10; int f[maxn],g[maxn]; void random(int t) { int i,j,k,m,num=0; for(i=0;i<t;i++)f[i]=i; m=t; for(i=0;i<t;i++) { num=rand()%m; g[i]=f[num]; f[num]=f[m-1]; m--; } } int judge() { int i,j,k=0; for(i=0;i<9;i++) { //printf("%c\n",e.c[i]); if(g[i]==0)continue; for(j=0;j<i;j++) { if(g[j]==0)continue; if(g[j]>g[i])k++; } } return k%2; } int main() { freopen("C:\\Documents and Settings\\All Users\\桌面\\in.txt","w",stdout); srand(time(NULL)); int i,j,k,n; printf("200\n"); for(i=0;i<200;i++) { random(9); k=judge(); for(j=0;j<9;j++) { if(g[j]==0) printf("X"); else printf("%d",g[j]); } printf("\n"); do { random(9); }while(k!=judge()); for(j=0;j<9;j++) { if(g[j]==0) printf("X"); else printf("%d",g[j]); } printf("\n"); } } */
相关文章推荐
- HDU 3567 Eight II 预处理+bfs+hash
- HDU 3567 八数码问题2 双BFS求解
- HDU 3567 Eight II 八数码(2)
- hdu1043八数码 bfs 打表/双向bfs/A*+康托判重+逆序奇偶剪枝
- HDU 3567 Eight II 八数码(2)
- HDU - 3567 Eight II (bfs预处理 + 康托) [kuangbin带你飞]专题二
- HDU(3567):八数码问题(升级版)——双BFS
- hdu 5440 Clock Adjusting(双向bfs)
- hdu 3085(双向bfs)
- HDU 3085 Nightmare 双向bfs
- HDU_1401——同步双向BFS,八进制位运算压缩,map存放hash
- HDU-1043:Eight(八数码+bfs(反向或A*))
- 【双向bfs】2017多校训练十 HDU 6171 Admiral
- HDU 1401 Solitaire(双向BFS)
- HDU 3567 Eight II 打表,康托展开,bfs,g++提交可过c++不可过 难度:3
- hdu 1401 Solitaire(双向bfs)
- 2017多校第10场 HDU 6171 Admiral 双向BFS或者A*搜索
- HDU-3567 Eight II
- hdu 1043 八数码 经典搜索问题 BFS+MAP
- hdu 1401 双向bfs