魔板

https://loj.ac/problem/10027

题目描述

  一块魔板由两行各四个数组成,初始状态为 :第一行:\(1,2,3,4\) ;第二行:\(5,6,7,8\)。并且魔板有\(3\)种操作,\(A\)为交换上下两行,\(B\)为将最右边一列转到最左侧,\(C\)为魔板中心顺时针旋转。给出目标状态,求出初始状态到目标状态的最少步数和操作序列。

思路

  这道题显然需要搜索,而求最小步数肯定用\(bfs\)。但这道题最难的判重,如何将魔板的状态压缩在一个\(hash\)数组中进行判重,这里我们可以引入康托展开。

  康托展开是针对于\(n\)的全排列,用于求全排列在所有顺序的排名。而魔板就可以看做一个\(8\)位的全排列。所以康托展开实质就是试图找有多少个全排列序列比已知序列要小。我们可以逐位分析,首先假设第一位为\(x\),那么第一位就比它小的有\((x - 1)* 7!\);同理,第二位为\(y\),那么第一位与它相同或第一位比它小的就需要考虑\(y\)\(x\)的大小关系,因为我们未知比\(y\)小的是否已填过。这里其实与数位\(dp\)有异曲同工之妙,都是分析是否与界限前几位相等来考虑。所以我们只需寻找在第\(i\)位后比这一位值小的有几个即可。

  有判重后正常打一遍\(bfs\)就完事了。

代码

#include <bits/stdc++.h>
using namespace std;
struct aa
{
    int s[10][10];
}st,goal;
int jc[15]={1,1,2,6,24,120,720,5040},step[50000],pre[50000],ans[50000],p;
bool vis[50000];
queue<aa>q;
int turn(aa x)                //康托展开
{
    int t[10],ret=0;
    for(int i=0;i<4;i++)t[i]=x.s[0][i];
    for(int i=3;i>=0;i--)t[7-i]=x.s[1][i];
    for(int i=0;i<8;i++)
    {
        int s=0;
        for(int j=i+1;j<8;j++)
            if(t[i]>t[j])s++;
        ret+=s*jc[7-i];
    }
    return ret;
}
aa change(int way,aa x)     //三种操作
{
    aa ret;
    if(way==1)
    {
        for(int i=0;i<4;i++)ret.s[1][i]=x.s[0][i];
        for(int i=0;i<4;i++)ret.s[0][i]=x.s[1][i];
    }
    else if(way==2)
    {
        ret.s[0][0]=x.s[0][3];ret.s[1][0]=x.s[1][3];
        for(int i=1;i<4;i++)
        {
            ret.s[0][i]=x.s[0][i-1];
            ret.s[1][i]=x.s[1][i-1];
        }
    }
    else if(way==3)
    {
        ret.s[0][0]=x.s[0][0];ret.s[1][0]=x.s[1][0];
        ret.s[0][1]=x.s[1][1];ret.s[1][1]=x.s[1][2];
        ret.s[0][2]=x.s[0][1];ret.s[1][2]=x.s[0][2];
        ret.s[0][3]=x.s[0][3];ret.s[1][3]=x.s[1][3];
    }
    return ret;
}
void print(int k)
{
    if(pre[k]==-1)return ;
    print(pre[k]);
    printf("%c",ans[k]-1+'A');
}
void bfs()
{
    int l=turn(st);
    q.push(st);step[l]=0;pre[l]=-1;
    while(!q.empty())
    {
        aa now=q.front();q.pop();
        int t=turn(now);
        for(int i=1;i<=3;i++)
        {
            aa nex=change(i,now);
            l=turn(nex);
            if(!vis[l])
            {
                q.push(nex);
                step[l]=step[t]+1;
                ans[l]=i;
                pre[l]=t;
                vis[l]=1;
                if(l==p){cout<<step[l]<<endl;print(l);return ;}
            }
        }
    }
}
int main() 
{
    for(int i=0;i<4;i++)st.s[0][i]=i+1;
    for(int i=3;i>=0;i--)st.s[1][i]=8-i;
    int l=turn(st);vis[l]=1;
    for(int i=0;i<4;i++)
        scanf("%d",&goal.s[0][i]);
    for(int i=3;i>=0;i--)
        scanf("%d",&goal.s[1][i]);
    p=turn(goal);
    if(l==p)
        printf("0");
    else
        bfs();
    return 0;
}
posted @ 2019-10-30 18:34  fbz  阅读(306)  评论(0编辑  收藏  举报