八数码问题(九宫格重排) 利用康托展开 判重

问题 E: [蓝桥杯][历届试题]九宫重排

时间限制: 1Sec 内存限制: 128MB 提交: 69 解决: 17

题目描述

如下面第一个图的九宫格中,放着  1~8  的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

我们把第一个图的局面记为:12345678. 
把第二个图的局面记为:123.46758 
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。 
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

输入

输入第一行包含九宫的初态,第二行包含九宫的终态。 

输出

输出最少的步数,如果不存在方案,则输出-1。

样例输入

12345678. 
123.46758 

样例输出

3

#include<bits/stdc++.h>
using namespace std;
string s,d;
bool vis[400000];
int FAC[11];
void init()
{
    FAC[0]=1;
    for(int i=1;i<=10;i++)FAC[i]=FAC[i-1]*i;
}
int cantor(string a, int n)
{
    int x = 0;
    for (int i = 0; i < n; ++i) {
        int smaller = 0;  // 在当前位之后小于其的个数
        for (int j = i + 1; j < n; ++j) {
            if (a[j] < a[i])
                smaller++;
        }
        x += FAC[n - i - 1] * smaller; // 康托展开累加
    }
    return x+1;  // 康托展开值
}
int ans=-1;
void bfs()
{
    int dx[4]={1,0,-1,0};
    int dy[4]={0,1,0,-1};
    queue<string>q;
    string t=s;
    q.push(t);
    q.push("");
    vis[cantor(t,9)]=1;
    int depth=0;int x,y;int x2,y2;int newpos;
    while(!q.empty())
    {
        t=q.front();q.pop();
        if(t==""){depth++;q.push("");continue;}
        if(t==d){ans=depth;break;}
        else
        {
            int pos=0;
            while(t[pos]!='0')pos++;
            pos++;
            x=(pos+2)/3;
            y=(pos-1)%3+1;
            for(int i=0;i<4;i++)
            {
                x2=x+dx[i];y2=y+dy[i];
                if(x2<1||x2>3||y2<1||y2>3)continue;
                newpos=(x2-1)*3+y2;
                swap(t[pos-1],t[newpos-1]);
                if(!vis[cantor(t,9)]){q.push(t);vis[cantor(t,9)]=1;}
                swap(t[pos-1],t[newpos-1]);
            }
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    init();
    cin>>s>>d;
    for(int i=0;i<9;i++)if(s[i]=='.')s[i]='0';
    for(int i=0;i<9;i++)if(d[i]=='.')d[i]='0';
    bfs();
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/linruier2017/article/details/81604654