HDU1430,魔板

书上的推荐例题,在双向广搜里面。刚开始尝试用双向广搜,但是一直wa,看网上讨论才知道所得到的答案不一定是最小的字典序。此题比较纠结的是标记,普通做要开8维数组,但是如果用cantor展开之后,每一种状态就可以用一个数字来代表,只用一个一维数组就可以标记了。

另外,要对输入做一下处理:将每一组的初态变成”12345678”,然后末态相应地变成“xxxxxxxx”,这样一来,每次的初态都一样,只是末态不一样,一次bfs()将所有的情况的结果都打表,然后每输入一个末态,直接输出答案,这样才不会超时。现在的问题是:初态变成”12345678“之后,怎样将末态变成相应的”xxxxxxxx”,答案才不会错。举个例子:比如初态是“63728145”,将初态处理成“12345678”之后,发生的改变是6->1,3->2,7->3,2->4,8->5,1->6,4->7,5->8假若末态为“54716328”,由于初态发生了改变,所以末态也要依照相同的规则进行改变,所以末态应该变为“54716328”->”87361245”,代码的实现为:


注:start[8]是初态,end[8]是末态,f[8]是对应的变换的值,比如上面就是f[6]=1

//处理f[8]数组
for (int i=0; i<8; i++)
{
    f[start[i]]=i+1;//上面就是f[6]=1,f[3]=2等等
}

//处理end[8]数组,start[8]不用管了,直接就是12345678
for (int i=0; i<8; i++)
{
    end[i]=f[end[i]];//原来是多少,按照f(x)进行变换
}


做好了上面的工作,下面的,应该问题就不大了,代码如下:

/*************************************************************************
    > File Name: main.cpp
    > Author:Eagles 
    > Mail:None 
    > Created Time: 2018年08月31日 星期五 14时21分37秒
    > Description:HDU1430 
 ************************************************************************/

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<string>
#include<algorithm>
using namespace std;

int factorial[]={1,1,2,6,24,120,720,5040,40320};//阶乘
bool vis[46300];//标记数组
string ans[46300];//打表

struct node
{
    char nums[2][4];//八个数字,我用的是char

    string s;//存储操作A、B、C

    void A()//A操作,下同
    {
        for (int i=0; i<4; i++)
        {
            char tmp=nums[0][i];
            nums[0][i]=nums[1][i];
            nums[1][i]=tmp;
        }
        s+='A';
    }

    void B()
    {
        char tmp1=nums[0][3];
        char tmp2=nums[1][3];

        for (int i=3; i>0;i--)
        {
            nums[0][i]=nums[0][i-1];
            nums[1][i]=nums[1][i-1];
        }

        nums[0][0]=tmp1;
        nums[1][0]=tmp2;

        s+='B';
    }

    void C()
    {
        char tmp=nums[0][1];

        nums[0][1]=nums[1][1];
        nums[1][1]=nums[1][2];
        nums[1][2]=nums[0][2];
        nums[0][2]=tmp;

        s+='C';
    }

    int cantor()//获得cantor值,就好比人的身份证,每个状态都只有唯一的值
    {
       int val=0;

        char tmp[8];

        for (int i=0; i<4; i++)
            tmp[i]=nums[0][i];

        for (int i=3; i>=0; i--)
            tmp[7-i]=nums[1][i];

        for (int i=0; i<7; i++)
        {
            int smaller=0;

            for (int j=i+1; j<8; j++)
                if(tmp[i]>tmp[j])
                    smaller++;

            val+=smaller*factorial[8-1-i];
        }

        return val;
    }
}s,e,cur,nex;//s是开始,e是结束,cur是当前,nex是下一个

void bfs()
{
    memset(vis,false,sizeof(vis));

    vis[s.cantor()]=true;
    ans[s.cantor()]='\n';

    queue<node>Q;

    Q.push(s);

    while (!Q.empty())
    {
        cur=Q.front();
        Q.pop();

        for (int i=0; i<3; i++)
        {
            nex=cur;

            switch(i)
            {
                case 0:
                    nex.A();break;
                case 1:
                    nex.B();break;
                case 2:
                    nex.C();break;
            }

            if(!vis[nex.cantor()])
            {
                vis[nex.cantor()]=true;
                ans[nex.cantor()]=nex.s;
                Q.push(nex);
            }
        }
    }
}

int main()
{
   // freopen("in.txt","r",stdin);
      //freopen("out.txt","w",stdout);

    char tmp[9];
    //使s的八个数字为"12345678"
    for (int i=0; i<4; i++)
        s.nums[0][i]=i+1+'0';
    for (int i=3; i>=0; i--)
        s.nums[1][i]=8-i+'0';

    bfs();

    while (cin>>tmp)
    {

        int f[9];

        for (int i=0; i<8; i++)
            f[tmp[i]-'0']=i+1;

        cin>>tmp;
    //使e的八个数字相应地改变
        for (int i=0; i<4; i++)
            e.nums[0][i]=f[tmp[i]-'0']+'0';
        for (int i=3; i>=0; i--)
            e.nums[1][i]=f[tmp[7-i]-'0']+'0';

        if (e.cantor()==0)
            cout<<endl;
        else
            cout<<ans[e.cantor()]<<endl;

    }

    return 0;
}

我当时还有一点比较疑惑,就是怕答案中出现类似”AA”,”BBBB”,”CCCC”的东西,但实际上不会出现,因为:出现AA,BBBB,CCCC,就回到上一步操作了,而上一步的vis[]数组已经被标记了,所以不会出现此类情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值