《算法笔记》8.2小节——搜索专题->广度优先搜索(BFS)问题 D: 【宽搜入门】魔板

题目描述

在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有8个大小相同的格子的魔板:
1 2 3 4
8 7 6 5
我们知道魔板的每一个方格都有一种颜色。这8种颜色用前8个正整数来表示。可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。对于上图的魔板状态,我们用序列(1,2,3,4,5,6,7,8)来表示。这是基本状态。
这里提供三种基本操作,分别用大写字母“A”,“B”,“C”来表示(可以通过这些操作改变魔板的状态):
“A”:交换上下两行;
“B”:将最右边的一列插入最左边;
“C”:魔板中央四格作顺时针旋转。
下面是对基本状态进行操作的示范:
A:
8 7 6 5
1 2 3 4
B:
4 1 2 3
5 8 7 6
C:
1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。
【输入格式】
输入有多组测试数据
只有一行,包括8个整数,用空格分开(这些整数在范围 1——8 之间),表示目标状态。
【输出格式】
Line 1: 包括一个整数,表示最短操作序列的长度。
Line 2: 在字典序中最早出现的操作序列,用字符串表示,除最后一行外,每行输出60个字符。

Sample Input

2 6 8 4 5 7 3 1

Sample Output

7

BCABCCB 

分析:简单的BFS。因为题目最后要输出序列,所以BFS时要用一个数组模拟队列,这样能在打到最终状态时,向前查找到之前的每一步。每次找到一步,将这次的操作压入栈中,最后一个个弹出,就能得到操作序列。

#include    <algorithm>
#include     <iostream>
#include      <cstdlib>
#include      <cstring>
#include       <string>
#include       <vector>
#include       <cstdio>
#include        <queue>
#include        <stack>
#include        <ctime>
#include        <cmath>
#include          <map>
#include          <set>
#include<unordered_map>
#define INF 0x3f3f3f3f
#define db1(x) cout<<#x<<"="<<(x)<<endl
#define db2(x,y) cout<<#x<<"="<<(x)<<", "<<#y<<"="<<(y)<<endl
#define db3(x,y,z) cout<<#x<<"="<<(x)<<", "<<#y<<"="<<(y)<<", "<<#z<<"="<<(z)<<endl
#define db4(x,y,z,a) cout<<#x<<"="<<(x)<<", "<<#y<<"="<<(y)<<", "<<#z<<"="<<(z)<<", "<<#a<<"="<<(a)<<endl
#define db5(x,y,z,a,r) cout<<#x<<"="<<(x)<<", "<<#y<<"="<<(y)<<", "<<#z<<"="<<(z)<<", "<<#a<<"="<<(a)<<", "<<#r<<"="<<(r)<<endl
using namespace std;

typedef struct node
{
    int cnt,fat,index;
    char kind;
}node;

int get_num(int temp[][4])
{
    int cnt=0;
    for(int i=0;i<4;++i)
        cnt=cnt*10+temp[0][i];
    for(int i=3;i>=0;--i)
        cnt=cnt*10+temp[1][i];
    return cnt;
}

int fun_A(int num[][4])
{
    int temp[2][4];
    for(int i=0;i<4;++i)
        temp[0][i]=num[1][i],temp[1][i]=num[0][i];
    return get_num(temp);
}

int fun_B(int num[][4])
{
    int temp[2][4];
    for(int i=1;i<4;++i)
        temp[0][i]=num[0][i-1],temp[1][i]=num[1][i-1];
    temp[0][0]=num[0][3],temp[1][0]=num[1][3];
    return get_num(temp);
}

int fun_C(int num[][4])
{
    int temp[2][4];
    for(int i=0;i<4;++i)
        temp[0][i]=num[0][i],temp[1][i]=num[1][i];
    int a=num[0][1],b=num[0][2],d=num[1][1],c=num[1][2];
    temp[0][1]=d,temp[0][2]=a,temp[1][2]=b,temp[1][1]=c;
    return get_num(temp);
}

void print(node num,vector<node>que,int index)
{
    printf("%d\n",num.index);
    stack<node>sta;sta.push(num);
    while(1)
    {
        node temp=sta.top();
        if(temp.fat==-1)break;
        temp=que[temp.fat];
        sta.push(temp);
    }
    int t=0,times=0;
    while(!sta.empty())
    {
        node temp=sta.top();sta.pop();
        if(temp.fat==-1)continue;
        printf("%c",temp.kind);times++;
        if(times==60)times=0,printf("\n");
    }
    return;
}

int main(void)
{
    #ifdef test
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    clock_t start=clock();
    #endif //test

    int fin=0;//fin代表最后的状态,数字是按照顺时针得到的
    for(int i=0;i<8;++i)
    {
        int a;scanf("%d",&a);
        fin=fin*10+a;
    }

    node fir;fir.cnt=12345678,fir.fat=-1,fir.index=0;
    unordered_map<int,int>ump;ump[12345678]=1;//初始状态已经出现过了
    vector<node>que;que.push_back(fir);//vector模拟队列

    int l=0,r=1,f=0;
    while(l<r)
    {
        int ll=l,rr=r;l=rr;
        for(ll;ll<rr;++ll)
        {
            node now=que[ll];
            if(now.cnt==fin)
            {
                f=1;
                print(now,que,ll);
                break;
            }

            int temp_now[2][4],temp_cnt=now.cnt;
            for(int i=0;i<4;++i)temp_now[1][i]=temp_cnt%10,temp_cnt/=10;
            for(int i=3;i>=0;--i)temp_now[0][i]=temp_cnt%10,temp_cnt/=10;

            node next_A,next_B,next_C;
            next_A.cnt=fun_A(temp_now),next_B.cnt=fun_B(temp_now),next_C.cnt=fun_C(temp_now);
            next_A.fat=next_B.fat=next_C.fat=ll;
            next_A.index=next_B.index=next_C.index=now.index+1;
            next_A.kind='A',next_B.kind='B',next_C.kind='C';

            if(ump[next_A.cnt]!=1)ump[next_A.cnt]=1,que.push_back(next_A),r++;
            if(ump[next_B.cnt]!=1)ump[next_B.cnt]=1,que.push_back(next_B),r++;
            if(ump[next_C.cnt]!=1)ump[next_C.cnt]=1,que.push_back(next_C),r++;
        }
        if(f)break;
    }

    #ifdef test
    clockid_t end=clock();
    double endtime=(double)(end-start)/CLOCKS_PER_SEC;
    printf("\n\n\n\n\n");
    cout<<"Total time:"<<endtime<<"s"<<endl;        //s为单位
    cout<<"Total time:"<<endtime*1000<<"ms"<<endl;    //ms为单位
    #endif //test
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值