CDOJ 485 UESTC 485 Game (八数码变形,映射,逆cantor展开)

题意:八数码,但是转移的方式是转动,一共十二种,有多组询问,初态唯一,终态不唯一。

题解:初态唯一,那么可以预处理出012345678的所有转移情况,然后将初态对012345678做一个映射,再枚举一下终态的所有情况,取最小值即可。

学了逆cantor展开,cantor展开是一个变进制数,每位上是原序列对应位置上的逆序值。那么求逆时候,就先除最大的位权得到对应位置上的逆序值,根据逆序值可以知道它在剩下的序列中第几大,然后标记它,迭代。状态转移有点麻烦。

#include<cstdio>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
//#define local

const int maxn = 362880;
int d[maxn];
int fac[9] = { 1,1,2,6,24,120,720,5040,40320};//,362880
int St[9],Ed[9];

int cantor(int *e) {
        int ret = 0;
        for(int i = 0; i < 9; i++) {
            int cnt = 0;
            for(int j = i+1; j < 9; j++)
                if(e[j] < e[i]) cnt++;
            ret += fac[8-i] * cnt;
        }
        return ret;
}

void invCantor(int *a,int code)
{
    bool vis[10] = {0};
    for(int i = 0; i < 9; i++){
        int t = code/fac[8-i];
        int j;
        for( j = 0; j < 9 ; j++){
            if(!vis[j]){
                if(t == 0) break;
                t--;
            }
        }
        a[i] = j; vis[j] = 1;
        code %= fac[8-i];
    }
}

int dir[12][9] = {
{2,0,1,3,4,5,6,7,8},{0,1,2,5,3,4,6,7,8},{0,1,2,3,4,5,8,6,7},
{1,2,0,3,4,5,6,7,8},{0,1,2,4,5,3,6,7,8},{0,1,2,3,4,5,7,8,6},
{6,1,2,0,4,5,3,7,8},{0,7,2,3,1,5,6,4,8},{0,1,8,3,4,2,6,7,5},
{3,1,2,6,4,5,0,7,8},{0,4,2,3,7,5,6,1,8},{0,1,5,3,4,8,6,7,2} };
queue<int> q;

void BfsPre()
{
    memset(d,-1,sizeof(d) );
    d[0] = 0;
    q.push(0);
    while(q.size()){
        int u = q.front();q.pop();
        int tmp[9];
        invCantor(tmp,u);
        for(int i = 0; i < 12; i++){
            int tmp2[9];
            for(int j = 0; j < 9; j++){
                tmp2[j] = tmp[dir[i][j]];
            }
            int v = cantor(tmp2);
            if(~d[v]) continue;
            d[v] = d[u]+1;
            q.push(v);
        }
    }
}


int query()
{
    int mp[9];
    for(int i = 0; i < 9; i++) { mp[St[i]] = i; }
    bool appear[9] = {0};
    for(int i = 0; i < 9; i++){
       if(~Ed[i]) appear[Ed[i] = mp[Ed[i]]] = 1;
    }
    int vec[9],sz = 0;
    for(int i = 0; i < 9; i++) {
        if(!appear[i]) vec[sz++] = i;
    }
    if(sz == 9)  return 0;
    if(sz == 0) return d[cantor(Ed)];
const int INF =  0x7fffffff;
    int ans = INF;
    int tmp[9];
     do{
        int j = 0;
        for(int i = 0; i < 9; i++){
            if(~Ed[i]) { tmp[i] = Ed[i]; }
            else { tmp[i] = vec[j++]; }
        }
        int Hash = cantor(tmp);
        if(~d[Hash]) ans = min(ans,d[Hash]);
    }while(next_permutation(vec,vec+sz));
    return ans!=INF? ans : -1;
}


int main()
{
#ifdef local
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
#endif // local
    int T;
    BfsPre();

    scanf("%d",&T);
    for(int k = 1; k <= T; k++){
        printf("Case #%d: ",k);
        for(int i = 0; i < 9; i ++){
            scanf("%d",St+i);
            St[i]--;
        }
        getchar();
        char buf[15];
        for(int i = 0; i < 3; i++){
            gets(buf);
            for(int j = 0; j < 5; j+=2){
                if('1'<=buf[j] && buf[j] <= '9'){
                    Ed[i*3+j/2] = buf[j] - '1';
                }else Ed[i*3+j/2] = -1;
            }
        }
        int ans = query();
        if(~ans) printf("%d\n",ans);
        else printf("No Solution!\n");
    }

    return 0;
}

 

posted @ 2015-07-26 22:22  陈瑞宇  阅读(295)  评论(0编辑  收藏  举报