八数码难题
——!x^n+y^n=z^n
我在此只说明此题的一种用BFS的方法,因为本人也是初学,勉勉强强写了一个单向的BFS,据说最快的是IDA*(然而蒟蒻我不会…)
各位如果想用IDA*的可以看看这位大佬的这篇文章:
http://www.cnblogs.com/ZYBGMZL/p/6852733.html
接下来是我的方法,用luogu的跑了最慢是200ms,感觉还行把。
题目描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入输出格式
输入格式:
输入初试状态,一行九个数字,空格用0表示
输出格式:
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
输入输出样例
输入样例#1:
283104765
输出样例#1:
4
因为这是要最优解且保证数据有解,于是就想到了BFS。
然而这个过程是有许多障碍的,要怎样检验自己的状态是否为解,还有判重的操作,如果你没有判重,TLE即在眼前…
所以我们可以想到压缩状态!当然如果用二进制难免有点力不从心,那我们干脆存成整数不就行了?但是可能你会发现,会有前导零的情况,怎么办?
这时候其实可以在状态前加一个1,在int型中还是过得去的。
那判重怎么搞?注意到这只有9!种状态。
想到什么?康托尔展开!对于0~8的全排列,
012345678 的字典序是1,如果让你手动操作我想没什么问题,那怎么让计算机做这件事?
对于 (a(n-1) a(n-2)L a(0))的字典序计算方法为:
Σ(ci*i!)ci为当前未出现的比ai小的数的个数。
既然这样,我们就能把状态一一存下来了,交换的话很简单,读者手动操作即可发现规律。
简单说明之后附上代码参考一下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define maxn 400000 //阶乘?我当然打表啦。 int fac[10]={1,1,2,6,24,120,720,5040,40320,362880}; //状态 struct sjs{ int num; int pos; }state[maxn]; //队列 struct _757{ int time; int now; int fd; }qu[maxn]; int cto[9]; //判重 bool rep[maxn]; int head,tail; //特殊嗜好??? namespace lys{ //快速幂 int fpow(int p){ int res=1,base=10; while(p>0){ if(p&1) res*=base; base*=base; p>>=1; } return res; } //计算cantor int cantor(int num){ int i=0,pos,res=0; int x=num; memset(cto,0,sizeof cto); while(num>0){ cto[i]=num%10; if(cto[i]==0) pos=i; num/=10; i++; } int j,cal; for(i=8;i>=0;i--){ cal=0; for(j=8;j>i;j--){ if(cto[j]<cto[i]) cal++; } res+=(cto[i]-cal)*fac[i]; } state[res+1].num=x; state[res+1].pos=pos; return res+1; } //判断是否能移动 bool chk(int pos,int i){ switch(i){ case 1:if(pos<6) return true ; return false ; case 2:if(pos>2) return true ; return false ; case 3:if(pos==8||pos==5||pos==2) return false ; return true ; case 4:if(pos==0||pos==3||pos==6) return false ; return true ; } } int bfs(){ int t=0,i,st,ch,fp,x,num; do{ st=qu[head].now; fp=fpow(state[st].pos); num=state[st].num; for(i=1;i<=4;i++){ if(chk(state[st].pos,i)){ switch(i){ case 1: x=(num/(fp*1000))%10; ch=num-x*fp*1000+x*fp; x=cantor(ch); qu[++tail].now=x; qu[tail].time=(qu[head].time+1); qu[tail].fd=head; //目标态,觉得不这样写也行,直接用num比较 if(x==46686){ return qu[tail].time; } if(rep[x]) tail--; else rep[x]=true ; break ; case 2: x=(num/(fp/1000))%10; ch=num-x*fp/1000+x*fp; x=cantor(ch); qu[++tail].now=x; qu[tail].time=(qu[head].time+1); qu[tail].fd=head; if(x==46686){ return qu[tail].time; } if(rep[x]) tail--; else rep[x]=true ; break ; case 3: x=(num/(fp*10))%10; ch=num+x*fp-x*fp*10; x=cantor(ch); qu[++tail].now=x; qu[tail].time=(qu[head].time+1); qu[tail].fd=head; if(x==46686){ return qu[tail].time; } if(rep[x]) tail--; else rep[x]=true ; break ; case 4: x=(num/(fp/10))%10; ch=num+x*fp-x*fp/10; x=cantor(ch); qu[++tail].now=x; qu[tail].time=(qu[head].time+1); qu[tail].fd=head; if(x==46686){ return qu[tail].time; } if(rep[x]) tail--; else rep[x]=true ; break ; } } } head++; }while(head<=tail); } int main(){ int i,j; char c; int st=0; for(i=1;i<=3;i++){ for(j=1;j<=3;j++){ c=getchar(); while(c<'0'||c>'9') c=getchar(); st=st*10+c-'0'; } } //初始状态 int r=cantor(1000000000+st); qu[++head].now=r; qu[head].time=0; rep[r]=true ; tail=1; printf("%d ",bfs()); return 0; } } int main(){ lys::main(); return 0; }