这题和八数码十分相似,或许是我的遗留问题吧....敲完总是不过,各种错误。迟迟下不了手的原因就在于对空间的把握问题。要节约空间就必须设计一个好的hash函数,不然时空开销大。关键在于哈希怎么弄了?八位数--->87654321如果采用这么大的空间,空间消耗太大,许多内存都没采用,不行...
后来查了下资料,发现有康托展开恰好是结果全排列的哈希问题。有了康托展开之后,全排列的问题就可以采用最小的空间消耗了,8!=40320<<87654321;这样就可以用bfs了。
当然8个数也可以用8*3bit来表示。这样面向字节流的操作很快,而且空间更省。
根据百度百科理解的康托展开:
int cantor( int num ) { int ret=0;bool h[9]={false}; for( int i=8;i>=2;i-- ) { int k,t,b=1; t=num; while( t/10>0 ){ k=t/10;t/=10;b*=10; } h[k]=true; num-=t*b; int p=0; for( int j=1;j<k;j++ ) if( h[j]==false ) p++; ret+=p*fec[i-1]; } //printf( "ret:%d\n",ret ); return ret; }当然还有逆康托展开,在理解了康托展开之后,逆展开也是很方便的。
就是当前的最左端的数,比其小的未用的数的个数,这些最小的数作为最左端的数,余下的数再进行全排列(只要看余下的位数,与可用的数的个数就可以了),比如54321,最左端是5,那么比他小的,最左端可以放4,3,2,1,都是未被使用过且小的。如果放下4,则剩下4个空填放1,2,3,5,种类数4!所以为4*4!;那么余下的4321,比他小的呢?这里就用到了递归,小化同样的情况。最终得出解。
理解了康托展开之后,逆康托展开也很方便了。
Code:
/* ID:sevenst4 LANG:C++ PROG:msquare */ #include<stdio.h> #include<cstdlib> using namespace std; struct node { int square[8]; char select; }S,E; int fec[10]={ 1,1,2,6,24,120,720,5040,40320 }; int hash[8]={1,2,3,4,8,7,6,5}; bool flag[50000]={false}; node queue[50000]; int pre[50000]; int nodeToNum( node a ) { int ret=0; for( int i=0;i<8;i++ ) ret=ret*10+a.square[hash[i]-1]; return ret; } int cantor( int num ) { int ret=0;bool h[9]={false}; for( int i=8;i>=2;i-- ) { int k,t,b=1; t=num; while( t/10>0 ){ k=t/10;t/=10;b*=10; } h[k]=true; num-=t*b; int p=0; for( int j=1;j<k;j++ ) if( h[j]==false ) p++; ret+=p*fec[i-1]; } //printf( "ret:%d\n",ret ); return ret; } void init() { for( int i=0;i<8;i++ ) { S.square[i]=hash[i]; scanf( "%d",&E.square[hash[i]-1] ); } } void print( node t,int foot ) { char r[100]; int step=0; for( int i=foot;pre[i]!=-1;i=pre[i] ) r[step++]=queue[i].select; printf( "%d\n",step ); int s=0; for( int i=step-1;i>=0;i-- ) { s++; if( s%60==0 ) printf( "\n" ); printf( "%c",r[i] ); } if( s%60!=0 ) printf( "\n" ); } node rotate( node a,int n ) { if( n==1 ) { for( int i=0;i<4;i++ ) { int t=a.square[i]; a.square[i]=a.square[i+4]; a.square[i+4]=t; } } else if( n==2 ) { int t1=a.square[3]; int t2=a.square[7]; for( int i=3;i>=0;i-- ) { a.square[i]=a.square[i-1]; a.square[i+4]=a.square[i+3]; } a.square[0]=t1; a.square[4]=t2; } else if( n==3 ) { int t=a.square[1]; a.square[1]=a.square[5]; a.square[5]=a.square[6]; a.square[6]=a.square[2]; a.square[2]=t; } return a; } void bfs() { int head=0,foot=1; queue[head]=S; if( nodeToNum(S)==nodeToNum(E) ) { printf( "0\n\n" ); return ; } flag[cantor(nodeToNum(S))]=true; int step=-1; pre[0]=-1; while( head!=foot ) { node cur=queue[head]; for( int i=1;i<=3;i++ ) { node temp=rotate( cur,i ); temp.select=i+'A'-1; int getnum=cantor(nodeToNum(temp)); if( flag[getnum]==false ) { flag[getnum]=true; pre[foot]=head; queue[foot++]=temp; } if( nodeToNum(temp)==nodeToNum(E) ) { print( temp,foot-1 ); return ; } } head++; } } int main() { freopen( "msquare.in","r",stdin ); freopen( "msquare.out","w",stdout ); init(); bfs(); return 0; }