HDU1430,魔板

Salvete發表於2018-08-31

書上的推薦例題,在雙向廣搜裡面。剛開始嘗試用雙向廣搜,但是一直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[]陣列已經被標記了,所以不會出現此類情況。

相關文章