zoukankan      html  css  js  c++  java
  • 魔板

    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;
    }
    
  • 相关阅读:
    Ruby单例方法和实例方法
    Silverlight本地化和全球化
    多线程 or 多进程 (转强力推荐)
    循环pthread_create导致虚拟内存上涨
    int在linux上的保存情况
    查看数据流的流程
    查看linux系统版本,内核,CPU,MEM,位数的相关命令(转)
    0/1背包问题
    linux下计算程序运行时间
    夸平台夸字符编码问题
  • 原文地址:https://www.cnblogs.com/fangbozhen/p/11766813.html
Copyright © 2011-2022 走看看