sicily 2011. Nine Digits(广搜,康托展开)

2011. Nine Digits

Constraints

Time Limit: 2 secs, Memory Limit: 256 MB

Description

 Nine tiles, each with a number from 1 to 9 on it, are packed into a 3 by 3 frame. Your task is to arrange the tiles so that they are ordered as:

1 2 3
4 5 6
7 8 9
 
       At each step, you can do the following operation to the tiles: Choose 2 by 2 tiles, rotate the tiles in clockwise order. For example:
       1 2 3             4 1 3                           1 2 3             1 2 3
       4 5 6      =>   5 2 6             or           4 5 6      =>   4 8 5
       7 8 9             7 8 9                           7 8 9             7 9 6
 

       Write a program to find the minimum number of steps.

Input

 Input contains multiple test cases.

       Each test case is a description of a configuration of the nine tiles. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and from left to right within a row, where the tiles are represented by numbers 1 to 9. For example:
       9 8 7
       6 5 4
       3 2 1
is described by this list:
       9 8 7 6 5 4 3 2 1

Output

 Output the minimum number of steps on a single line for each test case.

Sample Input

1 2 3 4 5 6 7 8 9
4 1 3 5 2 6 7 8 9

Sample Output

0
3

Problem Source

每周一赛:2010中山大学新手赛


这道题应该是考查广搜+康托压缩(康托展开),广搜用于找最短路,康托压缩是一个特殊的哈希函数,这个哈希函数好就好在它仅仅是一个大小为36W的int数组,能够省下不小内存。其实康托压缩就是把一个排列变成数字,这个数字是全排列中的第几个元素,比如132是123的第二个排列,就输出2。通过这个数字还能逆推回原来的序列。详情见百度百科:康托展开    有了这两个关键方法,设计算法的时候不要按部就班地从当前状态找到123456789这种顺序思路想,因为这样的时间复杂度和查询的次数是成线性关系增加的,会超时。应该从123456789这个状态出发,去找到其他所有状态,这个状态数为9!(9的全排列,包含了这个游戏所有的情况),而且搜索的时候数字变成了逆时针旋转(反过来)。这样只需要搜索一次。最后就是打表过程了。

#include <bits/stdc++.h>
using namespace std;

int fact[10] = {0,1,2,6,24,120,720,5040,40320,362880};//阶乘表
int src[10];
int dst[10] = {1,2,3,4,5,6,7,8,9};
int vis[400000];
int ans[400000];
struct pack{
    int num,k;
    pack(){}
    pack(int num,int k):num(num),k(k){}
};

int cantor(int num){
    int tmp[10];
    int code=0;
    for(int i=0;i<9;i++){
        tmp[8-i]=num%10;
        num/=10;
    }   
        for(int i = 0;i <= 8;++i)//*****康托展开公式:当前位数的逆序数*该位数的阶乘****(本题重点)  
    {  
        int cnt = 0;  
        for(int j = i + 1;j <= 8;j++)  
            if(tmp[i] > tmp[j])    ++cnt;  
        code += fact[8-i] * cnt;  
    } 
    return code;
}

void op_1(int a[],int &num){
    int tmp[10];
    tmp[0]=a[1];
    tmp[1]=a[4];
    tmp[2]=a[2];
    tmp[3]=a[0];
    tmp[4]=a[3];
    tmp[5]=a[5];
    tmp[6]=a[6];
    tmp[7]=a[7];
    tmp[8]=a[8];
    int hash=0;
    for(int i=8;i>=0;i--){
        hash+=tmp[i]*pow(10,8-i);
    }
    num = hash;
}
void op_2(int a[],int &num){
    int tmp[10];
    tmp[0]=a[0];
    tmp[1]=a[2];
    tmp[2]=a[5];
    tmp[3]=a[3];
    tmp[4]=a[1];
    tmp[5]=a[4];
    tmp[6]=a[6];
    tmp[7]=a[7];
    tmp[8]=a[8];
    int hash=0;
    for(int i=8;i>=0;i--){
        hash+=tmp[i]*pow(10,8-i);
    }
    num = hash;
}
void op_3(int a[],int &num){
    int tmp[10];
    tmp[0]=a[0];
    tmp[1]=a[1];
    tmp[2]=a[2];
    tmp[3]=a[4];
    tmp[4]=a[7];
    tmp[5]=a[5];
    tmp[6]=a[3];
    tmp[7]=a[6];
    tmp[8]=a[8];
    int hash=0;
    for(int i=8;i>=0;i--){
        hash+=tmp[i]*pow(10,8-i);
    }
    num = hash;
}
void op_4(int a[],int &num){
    int tmp[10];
    tmp[0]=a[0];
    tmp[1]=a[1];
    tmp[2]=a[2];
    tmp[3]=a[3];
    tmp[4]=a[5];
    tmp[5]=a[8];
    tmp[6]=a[6];
    tmp[7]=a[4];
    tmp[8]=a[7];
    int hash=0;
    for(int i=8;i>=0;i--){
        hash+=tmp[i]*pow(10,8-i);
    }
    num = hash;
}


void bfs(){
    memset(vis,0,sizeof(vis));
    queue<pack> box;
    int hash=0;
    for(int i=8;i>=0;i--){
        hash+=dst[i]*pow(10,8-i);
    }
    box.push(pack(hash,0));
    while(!box.empty()){
        int tmp[10];
        int sum=box.front().num;
        int k=box.front().k;
        //cout<<sum<<endl;
        box.pop();
        //判重 
        int hash=cantor(sum);
        if(vis[hash])
        continue;
        vis[hash]=1;
        ans[hash]=k;
        
        int cur[10];
        int sum_tmp;
        for(int i=0;i<9;i++){
            cur[8-i]=sum%10;
            sum/=10;
        }
        op_1(cur,sum_tmp);
        box.push(pack(sum_tmp,k+1));
        op_2(cur,sum_tmp);
        box.push(pack(sum_tmp,k+1));
        op_3(cur,sum_tmp);
        box.push(pack(sum_tmp,k+1));
        op_4(cur,sum_tmp);
        box.push(pack(sum_tmp,k+1));
    }
}
int main(){
    bfs();
    while(~scanf("%d",&src[0])){
        for(int i=1;i<9;i++)
        scanf("%d",&src[i]);
        int hash=0;
        for(int i=8;i>=0;i--){
            hash+=src[i]*pow(10,8-i);
        }
        printf("%d\n",ans[cantor(hash)]);
    }
}                                 




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值