最近做题时又遇到了八数码问题(见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 }