1449:【例题2】魔板
1449:【例题2】魔板
题解
这道题是一道非常典型的BFS题目,BFS所面临的最大问题是判断重复。显然,如果每次判
断重复都扫描一次队列,搜索的效率将非常低,会提高一个指数级,所以一般宽度搜索的判断重复
都是运用数组来实现的,那么数组下标就需要用一个Hash函数来计算出来。
本题Hash函数的设计:我们很容易想到将8个数字按顺时针顺序组合成8进制(每个数字都
减1)的8位基数。但是,这里最大的八进制数76543210,转换成十进制数是16434824,也就是说要
开大小为16434824的数组,如果题目强制空间限制,那么,数组的下标会越界
这里我们引入康托展开,即将一个排列对应成它在全排列中的序数,这样就不会MIE了
引入康托展开
设step[ ]记步数;记 i 的父结点为prt[ i ] ; a[ i ]表示第i个序列采用哪种变换。
代码
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<queue> #include<functional> #include<cstdlib> #include<utility> using namespace std; int jc[10]={1,1,2,6,24,120,720,5040}; int g,st,prt[50005],b[1000001]={0},step[50005]; char a[50005]; struct mb { int a[2][4]; }start,goal,q[90001]; int Turn(mb x) //康托展开 { int i,j,res=0,t[8],s; for(i=0;i<4;i++) t[i]=x.a[0][i]; //构成序列 for(i=3;i>=0;i--) t[7-i]=x.a[1][i]; for(i=0;i<8;i++) { s=0; for(j=i+1;j<=7;j++) if(t[j]<t[i]) s++; //找后面小于t[i]的值 res+=jc[7-i]*s; } return res; } mb Change(int way,int num) //三种操作 { mb temp; if(way==1) //A { for(int i=0;i<4;i++) temp.a [0][i]=q[num].a [1][i]; for(int i=0;i<4;i++) temp.a [1][i]=q[num].a [0][i]; return temp; } if(way==2) //B { temp.a [0][0]=q[num].a [0][3]; temp.a [1][0]=q[num].a [1][3]; for(int i=1;i<4;i++) temp.a [0][i]=q[num].a [0][i-1]; for(int i=1;i<4;i++) temp.a [1][i]=q[num].a [1][i-1]; return temp; } if(way==3) //C { temp.a [0][0]=q[num].a [0][0]; temp.a [0][3]=q[num].a [0][3]; temp.a [1][0]=q[num].a [1][0]; temp.a [1][3]=q[num].a [1][3]; temp.a [0][1]=q[num].a [1][1]; temp.a [0][2]=q[num].a [0][1]; temp.a [1][2]=q[num].a [0][2]; temp.a [1][1]=q[num].a [1][2]; return temp; } } void Print(int num) //递归出结果 { if(num==1) return ; Print(prt[num]); cout<<a[num]; } void Bfs() { int op=1,cl=1,i,t; //op,cl记录操作数,op记变化前,cl记变化后 mb temp; q[1]=start; step[1]=0; prt[1]=1; while(op<=cl) { for(i=1;i<=3;i++) //三种变换 { temp=Change(i,op); t=Turn(temp); if(!b[t]) //未标记,入队 { q[++cl]=temp; step[cl]=step[op]+1; b[t]=1; prt[cl]=op; //记录父节点,也就是上一步操作编号 a[cl]=char('A'+i-1); //记录操作类型 if(t==g) { cout<<step[cl]<<endl; Print(cl); return; } } } op++; } } int main() { int i; for(i=0;i<4;i++) start.a [0][i]=i+1; //初始化起始状态 for(i=3;i>=0;i--) start.a [1][i]=8-i; st=Turn(start); //标记起始状态 b[st]=1; for(i=0;i<4;i++) cin>>goal.a [0][i]; for(i=3;i>=0;i--) cin>>goal.a [1][i]; g=Turn(goal); if(g==st) { cout<<0; return 0; } Bfs(); return 0; }