HDU 1043(八数码问题)

来源:互联网 发布:用友软件怎么下载 编辑:程序博客网 时间:2024/06/05 15:06

题目大意:多个案例,每次无序的输入九个符号('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<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<string>#include<queue>#define ll long longusing 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;j<i;++j)            if(num[j]>num[i]) ++icount;        ans+=icount*fa[i];    }    return ans;}queue<digital>q;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;}


0 0
原创粉丝点击