1449:【例题2】魔板

1449:【例题2】魔板

题解

        这道题是一道非常典型的BFS题目,BFS所面临的最大问题是判断重复。显然,如果每次判
断重复都扫描一次队列,搜索的效率将非常低,会提高一个指数级,所以一般宽度搜索的判断重复
都是运用数组来实现的,那么数组下标就需要用一个Hash函数来计算出来。

        本题Hash函数的设计:我们很容易想到将8个数字按顺时针顺序组合成8进制(每个数字都
减1)的8位基数。但是,这里最大的八进制数76543210,转换成十进制数是16434824,也就是说要
开大小为16434824的数组,如果题目强制空间限制,那么,数组的下标会越界
这里我们引入康托展开,即将一个排列对应成它在全排列中的序数,这样就不会MIE了

        引入康托展开

 设step[ ]记步数;记 i 的父结点为prt[ i ] ; a[ i ]表示第i个序列采用哪种变换。

 

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<functional>
#include<cstdlib>
#include<utility> 

using namespace std;

int jc[10]={1,1,2,6,24,120,720,5040};
int g,st,prt[50005],b[1000001]={0},step[50005];
char a[50005];
struct mb
{
    int a[2][4];
}start,goal,q[90001];

int Turn(mb x)    //康托展开 
{
    int i,j,res=0,t[8],s;
    for(i=0;i<4;i++) t[i]=x.a[0][i];  //构成序列 
    for(i=3;i>=0;i--) t[7-i]=x.a[1][i];
    for(i=0;i<8;i++)
    {
        s=0;
        for(j=i+1;j<=7;j++)
          if(t[j]<t[i])  s++;   //找后面小于t[i]的值 
        res+=jc[7-i]*s;
    }
    return res;
}
    
mb Change(int way,int num)       //三种操作 
    {
        mb temp;
        if(way==1)   //A
        {
            for(int i=0;i<4;i++) temp.a [0][i]=q[num].a [1][i];
            for(int i=0;i<4;i++) temp.a [1][i]=q[num].a [0][i];
            return temp;
        } 
        
        if(way==2)   //B
        {
            temp.a [0][0]=q[num].a [0][3];
            temp.a [1][0]=q[num].a [1][3];
            for(int i=1;i<4;i++) temp.a [0][i]=q[num].a [0][i-1];
            for(int i=1;i<4;i++) temp.a [1][i]=q[num].a [1][i-1];
            return temp;
        } 
        
        if(way==3)    //C
        {
            temp.a [0][0]=q[num].a [0][0];
            temp.a [0][3]=q[num].a [0][3];
            temp.a [1][0]=q[num].a [1][0];
            temp.a [1][3]=q[num].a [1][3];
            temp.a [0][1]=q[num].a [1][1];
            temp.a [0][2]=q[num].a [0][1];
            temp.a [1][2]=q[num].a [0][2];
            temp.a [1][1]=q[num].a [1][2];
            return temp;
        } 
    }
    
void Print(int num)    //递归出结果 
    {
        if(num==1) return ;
        Print(prt[num]);
        cout<<a[num];
    }
    
void Bfs()
    {
        int op=1,cl=1,i,t;   //op,cl记录操作数,op记变化前,cl记变化后
        mb temp;
        q[1]=start;
        step[1]=0;
        prt[1]=1;
        while(op<=cl)
        {
            for(i=1;i<=3;i++)   //三种变换
            {
                temp=Change(i,op);
                t=Turn(temp);
                if(!b[t])     //未标记,入队
                {
                    q[++cl]=temp;
                    step[cl]=step[op]+1;
                    b[t]=1;
                    prt[cl]=op;     //记录父节点,也就是上一步操作编号
                    a[cl]=char('A'+i-1);   //记录操作类型
                    if(t==g)
                    {
                        cout<<step[cl]<<endl;
                        Print(cl);
                        return;
                    }
                }
            }
            
        op++;
        }
    
    }
    
 

int main()
{
    int i;
    for(i=0;i<4;i++) start.a [0][i]=i+1;     //初始化起始状态
    for(i=3;i>=0;i--) start.a [1][i]=8-i;
    st=Turn(start);    //标记起始状态
    b[st]=1;
    for(i=0;i<4;i++)
      cin>>goal.a [0][i];
    for(i=3;i>=0;i--)
      cin>>goal.a [1][i];
    g=Turn(goal);
    if(g==st)
    {
        cout<<0;
        return 0;
    }
    Bfs();
    return 0;
}

 

posted @ 2019-06-10 17:02  晔子  阅读(582)  评论(0编辑  收藏  举报