HDU 1043(八数码问题)

题目大意:多个案例,每次无序的输入九个符号('1'-'8'和'x')。这九个符号按输入顺序依次放入一个3*3的九宫格中。其中,符号'x'可以和上,下,左,右邻居交换位置,题目要求你通过这种交换使得九个符号呈1,2,3,4,5,6,7,8,x排列。要求你输出交换的顺序。(用'u','d','l','r'来表示上下左右)。然后题目是special judge(特判),即解决方法可能有多种,你输出任意一种即可。当达不到题目要求时输出'unsolvable'。


题解:一道八数码的题目,这里介绍其中一个解法:哈希+打表(比较水但简单的解法)。

       首先,题目解法是将9个数字('x'看成数字9)的每一种排列看成一种状态,则共有9!种不同的状态。我们从结果状态(1,2,3,4,5,6,7,8,9,)开始bfs搜索,每到达一个状态就将这个状态标记上(一是标记这个状态可以到达结果,二是标记访问了这个状态,防止重复访问,ps:利用一个flag数组来进行标记),标记的同时要记录这个状态到达前一状态的op(‘u,d,r,l’),记住要反向记录,因为我们是反向搜索(从结果开始搜索),此外我们还要记录这一状态的前一状态,这样每次才能从某一状态一直找到结果状态(递归查询)。bfs结束时我们便找到了所有可以到达结果的状态,并且记录下了路径,问题也基本得到解决。

然后说说细节。虽然此问题总共只有9!种状态(大约40万),但是我们要建立每个状态和唯一一个数字的联系,以方便我们的标记,这便要用到哈希。这里有个哈希的小技巧:全排列数化技术。首先,我们介绍一种新的进制方法。一般我们的进制方法都是以某个数k为基数,第n位的单位为k^(n-1),每一位到达k便进位。我们的这种新的进位是变进制数表示法。这种方法第n位的单位为n!,第n位到达n+1时便进位。因为对于任意n>0都有(n+1)!=n!*(n+1),所以这种进位方法完全可行。然后我们说明这种进制与全排列之间的联系。首先,n个数的全排列共有n!种状态,然后n-1位变进制的范围是[0,n!-1],刚好有n!个不同的数,也就是说两个东西的范围是一样的,下面我们来建立它们的一一对应。对于产生的任一排列 c1,c2,..,cn,其中第i个元素ci(2 <= i <= n)与它前面的i个元素构成的逆序对的个数为di(0 <= di < i),那么我们得到一个逆序数序列d1,d2,d3,...,d(n-1)(0 <= di <= i),而这与我们的变进制刚好对应(因为第i位满足di <= i)。于是我们可以用k=d1*1!+d2*2!+...+d(n-1)*(n-1)!来表示这个状态,并且对于任意不同的排列c1,c2,..,cn必有不同的逆序数序列d1,d2,d3,...,d(n-1)。证明如下:

对于全排列的任意两个不同的排列p0,p1,p2,...,pn(排列P)和q0,q1,q2,...,qn(排列Q),从后往前查找第一个不相同的元素,分别记为pi和qi(0 < i <= n)。
(1)如果qi > pi,那么,
如果在排列Q中qi之前的元素x与qi构成逆序对,即有x > qi,则在排列P中pi之前也有相同元素x > pi(因为x > qi且qi > pi),即在排列P中pi之前的元素x也与pi构成逆序对,所以pi的逆序数大于等于qi的逆序数。又qi与pi在排列P中构成pi的逆序对,所以pi的 逆序数大于qi的逆序数。
(2)同理,如果pi > qi,那么qi的逆序数大于pi的逆序数。
因此,由(1)和(2)知,排列P和排列Q对应的变进制数至少有第i位不相同,即全排列的任意两个不同的排列具有不同的变进制数。至此得证。

       至此,我们可以获取任意排列的哈希值。

下面是代码:

#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const int inf=0x08080808;
const int maxn=4e5;
char str[40],op[maxn],cc[4]={'u','d','l','r'};
int fa[10],flag[maxn],nn[maxn];
int mo[4][2]={1,0,-1,0,0,1,0,-1};
struct digital{
    int num[10];
    int pos;
};
int hash_val(int num[]){
    int ans=0;
    for(int i=1;i<9;++i){
        int icount=0;
        for(int j=0;jnum[i]) ++icount;
        ans+=icount*fa[i];
    }
    return ans;
}
queueq;
bool is_norm(int x,int y,int mx,int my){
    if(x+mx>=0&&x+mx<3&&y+my>=0&&y+my<3) return true;
    return false;
}

void bfs(){
    digital temp;
    for(int i=0;i<9;++i) temp.num[i]=i+1;
    temp.pos=8;
    int ha=hash_val(temp.num),hp;
    flag[ha]=1;
    op[ha]='\0';
    q.push(temp);
    while(!q.empty()){
        temp=q.front();
        q.pop();
        int pos=temp.pos;
        hp=hash_val(temp.num);
        int x=pos/3,y=pos%3;
        for(int i=0;i<4;++i){
            if(is_norm(x,y,mo[i][0],mo[i][1])){
                int new_pos=(x+mo[i][0])*3+y+mo[i][1];
                digital t;
                for(int j=0;j<9;++j) t.num[j]=temp.num[j];
                t.pos=new_pos;
                t.num[pos]=temp.num[new_pos];
                t.num[new_pos]=temp.num[pos];
                ha=hash_val(t.num);
                if(flag[ha]==0){
                    flag[ha]=1;
                    nn[ha]=hp;
                    op[ha]=cc[i];
                    q.push(t);
                }
            }
        }
    }
}
int main(){
    fa[0]=1;
    for(int i=1;i<9;++i)
        fa[i]=fa[i-1]*i;
    memset(flag,0,sizeof(flag));
    bfs();
    while(scanf("%c",&str[0])!=EOF){
        int num[10],ha;
        if(str[0]=='x') num[0]=9;
        else num[0]=str[0]-'0';
        for(int i=1;i<9;i++){
            cin>>str[i];
            if(str[i]=='x') num[i]=9;
            else num[i]=str[i]-'0';
        }
        ha=hash_val(num);
        if(flag[ha]){
            while(op[ha]){
                printf("%c",op[ha]);
                ha=nn[ha];
            }
            printf("\n");
        }
        else printf("unsolvable\n");
        getchar();
    }
    return 0;
}


你可能感兴趣的:(HDU)