[ZJOI2005]九数码游戏(BFS+hash)
Solution
这题的话直接上BFS就可以了,因为要输出方案,所以我们要开一个pre数组记录前驱,最后输出就可以了。
对于状态的记录,一般都用哈希来存,但因为这道题比较特殊,它是一个排列,所以我们可以利用康拓展开把空间压到9!。
康拓展开
一个排列的康拓展开表示的是字典序比他小的排列的个数,所以我们统计一下每一位后面有几个比它小的数字,乘上(n-i)!
inline int zx_hash(int x){ for(int i=9;i>=1;--i)a[i]=x%10,x/=10; int num=0; for(int i=1;i<=9;++i){ int aa=0; for(int j=i+1;j<=9;++j)if(a[i]>a[j])aa++; num+=aa*jie[9-i]; } return num; }
逆康拓展开
我们不但要支持把排列映射成数字,还要支持把数字映射成排列。
具体操作就是从高到低按位考虑,令x=num/(n-i)!,那么可选集合中有x个数是比这一位上的数字小的,所以我们选择第x+1个数。
inline int anti_hash(int x){ int num=0; for(int i=0;i<=8;++i)vec[i]=i;int zo=8; for(int i=8;i>=0;--i){ int y=x/jie[i]; x=x%jie[i]; num=num*10+vec[y]; for(int j=y;j<zo;++j)vec[j]=vec[j+1];zo--; } return num; }
不过康拓展开的复杂度是n^2的,但常数较小,遇到哈希排列之类的问题试一下。
Code
#include<iostream> #include<cstdio> #include<queue> #include<vector> #include<algorithm> #define mm make_pair #define N 12 using namespace std; const int r1[10]={0,2,3,6,1,5,9,4,7,8}; const int r2[10]={0,1,2,3,5,6,4,7,8,9}; int jie[N],a[N],d1[N],d2[N],x,win,ans[50],ji[400000],tag,tot,vec[10]; struct node{ int first,second; }; queue<node>q; inline int zx_hash(int x){ for(int i=9;i>=1;--i)a[i]=x%10,x/=10; int num=0; for(int i=1;i<=9;++i){ int aa=0; for(int j=i+1;j<=9;++j)if(a[i]>a[j])aa++; num+=aa*jie[9-i]; } return num; } inline int anti_hash(int x){ int num=0; for(int i=0;i<=8;++i)vec[i]=i;int zo=8; for(int i=8;i>=0;--i){ int y=x/jie[i]; x=x%jie[i]; num=num*10+vec[y]; for(int j=y;j<zo;++j)vec[j]=vec[j+1];zo--; } return num; } int main(){ for(int i=1;i<=9;++i)scanf("%d",&a[i]),x=x*10+a[i];jie[0]=1;int mem=x; for(int i=1;i<=9;++i)jie[i]=jie[i-1]*i; win=zx_hash(123456789); q.push(node{zx_hash(x),0}); while(!q.empty()){ int u=q.front().first,nn=q.front().second;q.pop(); if(u==win){ printf("%d\n",nn); tag=1; break; } x=anti_hash(u); for(int i=9;i>=1;--i)d1[r1[i]]=x%10,d2[r2[i]]=x%10,x/=10; int x1=0,x2=0; for(int i=1;i<=9;++i)x1=x1*10+d1[i],x2=x2*10+d2[i]; x1=zx_hash(x1);x2=zx_hash(x2); if(!ji[x1])ji[x1]=u,q.push(node{x1,nn+1}); if(!ji[x2])ji[x2]=u,q.push(node{x2,nn+1}); } if(!tag){ printf("UNSOLVABLE"); return 0; } x=mem;x=zx_hash(x); while(win!=x){ ans[++tot]=win;win=ji[win]; } ans[++tot]=x; for(int i=tot;i>=1;--i){ int qq=anti_hash(ans[i]); for(int j=9;j>=1;--j)a[j]=qq%10,qq/=10; printf("%d %d %d\n%d %d %d\n%d %d %d\n\n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]); } return 0; }