USACO 3.2 Magic Squares 【搜索+康托展开】
题目背景
在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
题目描述
我们知道魔板的每一个方格都有一种颜色。这8种颜色用前8个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。
这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示(可以通过这些操作改变魔板的状态):
“A”:交换上下两行;
“B”:将最右边的一列插入最左边;
“C”:魔板中央四格作顺时针旋转。
下面是对基本状态进行操作的示范:
A:
1 2 3 4 ==> 8 7 6 5
8 7 6 5 ==> 1 2 3 4
B:
1 2 3 4 ==> 4 1 2 3
8 7 6 5 ==> 5 7 6 5
C:
1 2 3 4 ==> 1 7 2 4
8 7 6 5 ==> 8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。
分析
这题应当就是搜索了,但是到底是深搜还是广搜呢?注意到题目中说是要“用最少的基本操作”,也就是要求最短路径,那么就一定是广搜啦。但一定记住要在每次搜索的时候判重,那么我们如何记录这八个方块的状态呢?显然是可以用一个八维数组的,但是还可以有一种更为普遍的方法,那就是康托展开:
X=a[n](n-1)!+a[n-1](n-2)!+…+a[i]*(i-1)!+…+a[1]*0! 其中a[i]为当前未出现的元素中是排在第几个(从0开始)。
没错,它其实就是另一种特殊的字符串哈希,本质是把一个字符串中每个字符的位置和信息结合在一起,从而确立一个字符串的唯一性。有了这个方法,我们就可以AC了。
下面是参考代码:
/* ID: linda_f1 PROG: msquare LANG: C++ */ #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; int fac[9]={1,1,2,6,24,120,720,5040,40320};//contor struct point { int a[10]; int n; int step; }p[800000]; int goal,bas,tot=0; int f[40320*3]; bool vis[40320*3]; int contor(int t) { int num=0,k=0; for(int i=1;i<=8;i++) { k=0; for(int j=i+1;j<=8;j++) if(p[t].a[i]>p[t].a[j]) k++; num+=k*fac[8-i]; } return num; } void init() { for(int i=1;i<=4;i++) p[1].a[i]=i; for(int i=5;i<=8;i++) p[1].a[i]=13-i; vis[contor(1)]=1; } void out(int t) { if(t==1) return; out(f[t]); if(f[t]==1) cout<<tot<<endl; if(p[t].n==1) cout<<"A"; if(p[t].n==2) cout<<"B"; if(p[t].n==3) cout<<"C"; } void A(int t) { swap(p[t].a[1],p[t].a[5]); swap(p[t].a[2],p[t].a[6]); swap(p[t].a[3],p[t].a[7]); swap(p[t].a[4],p[t].a[8]); } void B(int t) { swap(p[t].a[4],p[t].a[3]);swap(p[t].a[8],p[t].a[7]); swap(p[t].a[3],p[t].a[2]);swap(p[t].a[7],p[t].a[6]); swap(p[t].a[2],p[t].a[1]);swap(p[t].a[6],p[t].a[5]); } void C(int t) { swap(p[t].a[2],p[t].a[3]); swap(p[t].a[2],p[t].a[7]); swap(p[t].a[2],p[t].a[6]); } void bfs() { int h=0,t=1; p[1].step=0; while(h<=t) { h++; for(int i=1;i<=3;i++) { t++; for(int j=1;j<=8;j++) p[t].a[j]=p[h].a[j]; if(i==1) A(t); if(i==2) B(t); if(i==3) C(t); bas=contor(t); if(vis[bas]) t--; else { vis[bas]=1; p[t].step=p[h].step+1; p[t].n=i; tot=p[t].step; f[t]=h; if(goal==bas) { out(t); return ; } } } } } int main() { // freopen("msquare.in","r",stdin); // freopen("msquare.out","w",stdout); init();//建立基本态 for(int i=1;i<=4;i++) cin>>p[0].a[i]; for(int i=5;i<=8;i++) cin>>p[0].a[13-i]; goal=contor(0); if(vis[goal]) { cout<<0<<endl; return 0; } bfs(); cout<<endl; return 0; }