AcWing 1107 魔板

题目描述:

Rubik 先生在发明了风靡全球的魔方之后,又发明了它的二维版本——魔板。

这是一张有 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:魔板中央对的4个数作顺时针旋转。

下面是对基本状态进行操作的示范:

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 个整数,用空格分开,表示目标状态。

输出格式

输出文件的第一行包括一个整数,表示最短操作序列的长度。

如果操作序列的长度大于0,则在第二行输出字典序最小的操作序列。

数据范围

输入数据中的所有数字均为 1 到 8 之间的整数。

输入样例:

2 6 8 4 5 7 3 1

输出样例:

7
BCABCCB

分析:

本题是求从一种状态到另一种状态的最小转化步数,并且还要输出每次转化进行的操作,BFS除了能够解决边权相同的最短路径问题,也可以解决从一种状态转移到另一种状态的最小步数问题。只需要将起始状态放入队列,然后将队头状态能够到达的状态再依次加入队列,以此类推,直至搜索到结束状态为止。本题只能从开始状态开始搜索,这里的三种操作ABC,尽管状态s经过A操作转化为状态t,状态t再做一次A操作又能够回到状态s,A操作是可逆的,但是BC操作都是不可逆的,因此不能倒着搜索。

本题的难度在于状态的表示和存储。首先是如何存储当前状态,直接用个八位的整数,用个vector,或者用个普通数组都是可以的,但是这些表示不便于存储状态附带的信息或者不方便转化成其他状态,比如用八位数的整数存储状态,那么还需要手动分离出每一位才能进行ABC操作,比较麻烦,用vector或者数组存储后面再表示状态附带的信息,比如步数和上一步状态时就很不方便了。所以这里用string存储状态。然后考虑如何实现ABC三种操作,只需要观察每种操作对各个位置数的影响即可,然后用string的库函数重新拼凑就行了,三种操作的实现可以参考代码。接下来考虑步数和上一步状态信息的存储,string类型对应的步数自然可以用unordered_map<string,int>进行存储,而上一步的状态以及从上一步转化到这一步的操作类型就可以用unordered_map<string,pair<char,string>>进行存储,char存储操作类型,后面的string存储上一步的状态。具体的实现细节见代码:

#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <string>
using namespace std;
string q[40500],start = "12345678";
unordered_map<string,int> d;
unordered_map<string,pair<char,string> > pre;
string move1(string s){
    return string(s.rbegin(),s.rend());
}
string move2(string s){
    return s[3] + s.substr(0,3) + s.substr(5,3) + s[4];
}
string move3(string s){
    return s.substr(0,1)+s[6]+s[1]+s.substr(3,2)+s[2]+s[5]+s[7];
}
void bfs(string end){
    int hh = 0,tt = 0;
    q[0] = start,d[start] = 0;
    while(hh <= tt){
        string t = q[hh++],s[3];
        s[0]=move1(t),s[1]=move2(t),s[2]=move3(t);
        for(int i = 0;i < 3;i++){
            if(!d.count(s[i])){
                d[s[i]] = d[t] + 1;
                pre[s[i]] = {'A' + i,t};
                if(s[i] == end)   return;
                q[++tt] = s[i];
            }
        }
    }
}
int main(){
    int n;
    string res,end;
    for(int i = 0;i < 8;i++){
        cin>>n;
        end += '0' + n;
    }
    bfs(end);
    cout<<d[end]<<endl;
    while(start != end){
        res += pre[end].first;
        end = pre[end].second;
    }
    cout<<string(res.rbegin(),res.rend());
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值