最近做题时又遇到了八数码问题(见POJ1077),记得高中搞OI时也做过这道题,AC的时候内牛满面。其实这道题用朴素的广搜即可AC,只不过记录各个状态比较麻烦,如果把字符串当成一个列数直接存储的话需要10e9个数,这样显然是会爆内存的。所以很容易想到建立hash表把每个状态一一映射,这样只有9!个状态,关键问题在于hash函数的构造。当初做这题的时候是借鉴了别人的hash函数,也没怎么懂原理,仿造着写上去调了一下午也AC了。其实像这种全排列的问题有一种特殊的hash函数,即康托展开。
举个例子,{1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。如我想知道321是{1,2,3}中第几个大的数可以这样考虑 :第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个。所以321是第6个大的数。 2*2!+1*1!+1*0!就是康托展开。再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2 1*2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,1324是第三个大数。这样我们就得到了我们很容易得到一个全排列数到hash函数映射,即X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!,其中a[i]是在第I位后比第I位小的数字个数。
顺便贴一下1077的源代码
1 # include<stdio.h> 2 int mark[363000]={0}; 3 int x[363000],father[363000]; 4 char ans[363000]; 5 int map[363000][10]; 6 int fact[9]={0,1,2,6,24,120,720,5040,40320}; 7 int hash(int st); 8 void printans(int st); 9 int main(void) 10 { 11 int i,s,t,tempd,markj; 12 char temp; 13 for (i=1;i<=9;i++) 14 { 15 scanf("%c",&temp); 16 getchar(); 17 if ('x'==temp) 18 { 19 map[1][i]=9; 20 x[1]=i; 21 } 22 else 23 map[1][i]=temp-'0'; 24 } 25 mark[hash(1)]=1; markj=0; 26 s=0; t=1; 27 do 28 { 29 s++; 30 if (x[s]!=1&&x[s]!=2&&x[s]!=3) 31 { 32 t++; 33 for (i=1;i<=9;i++) 34 map[t][i]=map[s][i]; 35 x[t]=x[s]; 36 map[t][x[t]]=map[t][x[t]-3]; 37 map[t][x[t]-3]=9; 38 x[t]=x[t]-3; 39 ans[t]='u'; 40 father[t]=s; 41 tempd=hash(t); 42 if (mark[tempd]) 43 t--; 44 else mark[tempd]=1; 45 if (0==tempd) 46 { 47 printans(t); 48 markj=1; 49 break; 50 } 51 } 52 if (x[s]!=7&&x[s]!=8&&x[s]!=9) 53 { 54 t++; 55 for (i=1;i<=9;i++) 56 map[t][i]=map[s][i]; 57 x[t]=x[s]; 58 map[t][x[t]]=map[t][x[t]+3]; 59 map[t][x[t]+3]=9; 60 x[t]=x[t]+3; 61 ans[t]='d'; 62 father[t]=s; 63 tempd=hash(t); 64 if (mark[tempd]) 65 t--; 66 else mark[tempd]=1; 67 if (0==tempd) 68 { 69 printans(t); 70 markj=1; 71 break; 72 } 73 } 74 if (x[s]!=1&&x[s]!=4&&x[s]!=7) 75 { 76 t++; 77 for (i=1;i<=9;i++) 78 map[t][i]=map[s][i]; 79 x[t]=x[s]; 80 map[t][x[t]]=map[t][x[t]-1]; 81 map[t][x[t]-1]=9; 82 x[t]=x[t]-1; 83 ans[t]='l'; 84 father[t]=s; 85 tempd=hash(t); 86 if (mark[tempd]) 87 t--; 88 else mark[tempd]=1; 89 if (0==tempd) 90 { 91 printans(t); 92 markj=1; 93 break; 94 } 95 } 96 if (x[s]!=3&&x[s]!=6&&x[s]!=9) 97 { 98 t++; 99 for (i=1;i<=9;i++) 100 map[t][i]=map[s][i]; 101 x[t]=x[s]; 102 map[t][x[t]]=map[t][x[t]+1]; 103 map[t][x[t]+1]=9; 104 x[t]=x[t]+1; 105 ans[t]='r'; 106 father[t]=s; 107 tempd=hash(t); 108 if (mark[tempd]) 109 t--; 110 else mark[tempd]=1; 111 if (0==tempd) 112 { 113 printans(t); 114 markj=1; 115 break; 116 } 117 } 118 }while (s<t); 119 if (!markj) printf("unsolvable"); 120 return 0; 121 } 122 123 int hash(int st) 124 { 125 int i,j,list,sum; 126 sum=0; 127 for (i=1;i<=8;i++) 128 { 129 list=0; 130 for (j=i+1;j<=9;j++) 131 if (map[st][j]<map[st][i]) 132 list++; 133 sum+=list*fact[9-i]; 134 } 135 return sum; 136 } 137 138 void printans(int st) 139 { 140 if (st!=1) 141 { 142 printans(father[st]); 143 printf("%c",ans[st]); 144 } 145 }
浙公网安备 33010602011771号