[ZJOI2005]九数码游戏(BFS+hash)

Solution

这题的话直接上BFS就可以了,因为要输出方案,所以我们要开一个pre数组记录前驱,最后输出就可以了。

对于状态的记录,一般都用哈希来存,但因为这道题比较特殊,它是一个排列,所以我们可以利用康拓展开把空间压到9!。

康拓展开

一个排列的康拓展开表示的是字典序比他小的排列的个数,所以我们统计一下每一位后面有几个比它小的数字,乘上(n-i)!

inline int zx_hash(int x){
    for(int i=9;i>=1;--i)a[i]=x%10,x/=10;
    int num=0;
    for(int i=1;i<=9;++i){
        int aa=0;
        for(int j=i+1;j<=9;++j)if(a[i]>a[j])aa++;
        num+=aa*jie[9-i]; 
    }
    return num;
}

逆康拓展开

我们不但要支持把排列映射成数字,还要支持把数字映射成排列。

具体操作就是从高到低按位考虑,令x=num/(n-i)!,那么可选集合中有x个数是比这一位上的数字小的,所以我们选择第x+1个数。

inline int anti_hash(int x){
   int num=0;
   for(int i=0;i<=8;++i)vec[i]=i;int zo=8;
   for(int i=8;i>=0;--i){
         int y=x/jie[i];
         x=x%jie[i];
         num=num*10+vec[y];
         for(int j=y;j<zo;++j)vec[j]=vec[j+1];zo--;
   } 
 return num;
}

不过康拓展开的复杂度是n^2的,但常数较小,遇到哈希排列之类的问题试一下。

Code

 

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#define mm make_pair
#define N 12
using namespace std;
const int r1[10]={0,2,3,6,1,5,9,4,7,8};
const int r2[10]={0,1,2,3,5,6,4,7,8,9}; 
int jie[N],a[N],d1[N],d2[N],x,win,ans[50],ji[400000],tag,tot,vec[10];
struct node{
    int first,second;
};
queue<node>q;
inline int zx_hash(int x){
    for(int i=9;i>=1;--i)a[i]=x%10,x/=10;
    int num=0;
    for(int i=1;i<=9;++i){
        int aa=0;
        for(int j=i+1;j<=9;++j)if(a[i]>a[j])aa++;
        num+=aa*jie[9-i]; 
    }
    return num;
}
inline int anti_hash(int x){
   int num=0;
   for(int i=0;i<=8;++i)vec[i]=i;int zo=8;
   for(int i=8;i>=0;--i){
         int y=x/jie[i];
         x=x%jie[i];
         num=num*10+vec[y];
         for(int j=y;j<zo;++j)vec[j]=vec[j+1];zo--;
   } 
 return num;
}
int main(){
    for(int i=1;i<=9;++i)scanf("%d",&a[i]),x=x*10+a[i];jie[0]=1;int mem=x;
    for(int i=1;i<=9;++i)jie[i]=jie[i-1]*i;
    win=zx_hash(123456789);
    q.push(node{zx_hash(x),0});
    while(!q.empty()){
        int u=q.front().first,nn=q.front().second;q.pop();
        if(u==win){
            printf("%d\n",nn);
            tag=1;
            break;
        }    
        x=anti_hash(u);
        for(int i=9;i>=1;--i)d1[r1[i]]=x%10,d2[r2[i]]=x%10,x/=10;
        int x1=0,x2=0; 
        for(int i=1;i<=9;++i)x1=x1*10+d1[i],x2=x2*10+d2[i];
        x1=zx_hash(x1);x2=zx_hash(x2);
        if(!ji[x1])ji[x1]=u,q.push(node{x1,nn+1});
        if(!ji[x2])ji[x2]=u,q.push(node{x2,nn+1}); 
    }
    if(!tag){
        printf("UNSOLVABLE");
        return 0;
    }
    x=mem;x=zx_hash(x);
    while(win!=x){
        ans[++tot]=win;win=ji[win];
    }
    ans[++tot]=x;
    for(int i=tot;i>=1;--i){
        int qq=anti_hash(ans[i]);
        for(int j=9;j>=1;--j)a[j]=qq%10,qq/=10;
        printf("%d %d %d\n%d %d %d\n%d %d %d\n\n",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
    }
    return 0;
}

 

posted @ 2018-10-11 15:09  comld  阅读(247)  评论(0编辑  收藏  举报