zoukankan      html  css  js  c++  java
  • [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
    ",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
    %d %d %d
    %d %d %d
    
    ",a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]);
        }
        return 0;
    }
  • 相关阅读:
    谈谈关系类并查集
    并查集的应用
    关于欧拉线筛
    bzo4802 欧拉函数 miller_rabin pollard_rho
    数论
    前端切图:自制简易音乐播放器
    前端切图:自制简易音乐播放器
    SEO那些事:一句代码一键分享网站
    SEO那些事:一句代码一键分享网站
    前端调用百度API
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/9772524.html
Copyright © 2011-2022 走看看