题目
题目描述
原题来自:USACO 3.2.5
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:魔板中央作顺时针旋转。
下面是对基本状态进行操作的示范:
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 之间)不换行,表示目标状态。
输出格式
输出文件的第一行包括一个整数,表示最短操作序列的长度。 第二行为在字典序中最早出现的操作序列。
样例
输入
2 6 8 4 5 7 3 1
输出
7
BCABCCB
代码详解
#include<bits/stdc++.h>
using namespace std;
struct mb{
string ans;
char xl[8];
}fir;
char fin[8];
queue<mb> q;
unordered_set<int> ss;
mb A(mb);
mb B(mb);
mb C(mb);
bool vis(mb);
void bfs();
int main()
{
fir.ans="";
for(char i='1';i<='8';i++){
fir.xl[i-'0'-1]=i;
}
for(int i=0;i<8;i++){
cin>>fin[i];
getchar();
}
q.push(fir);
bfs();
return 0;
}
mb A(mb t){
mb tt;
tt.ans=t.ans+'A';
for(int i=0;i<8;i++){
tt.xl[i]=t.xl[7-i];
}
return tt;
}
mb B(mb t){
mb tt;
tt.ans=t.ans+'B';
tt.xl[0]=t.xl[3];
tt.xl[1]=t.xl[0];
tt.xl[2]=t.xl[1];
tt.xl[3]=t.xl[2];
tt.xl[4]=t.xl[5];
tt.xl[5]=t.xl[6];
tt.xl[6]=t.xl[7];
tt.xl[7]=t.xl[4];
return tt;
}
mb C(mb t){
mb tt;
tt.ans=t.ans+'C';
for(int i=0;i<8;i++){
tt.xl[i]=t.xl[i];
}
tt.xl[1]=t.xl[6];
tt.xl[2]=t.xl[1];
tt.xl[5]=t.xl[2];
tt.xl[6]=t.xl[5];
return tt;
}
bool vis(mb t){
int l;
sscanf(t.xl,"%d",&l);
if(!ss.count(l)){
ss.emplace(l);
return true;
}
return false;
}
bool check(mb t){
for(int i=0;i<8;i++){
if(t.xl[i]!=fin[i]){
return false;
}
}
return true;
}
void bfs(){
while(!q.empty()){
mb now=q.front();
q.pop();
if(check(now)){
cout<<now.ans.size()<<endl<<now.ans;
return;
}
mb tmp=A(now);
if(vis(tmp)){
q.push(tmp);
}
tmp=B(now);
if(vis(tmp)){
q.push(tmp);
}
tmp=C(now);
if(vis(tmp)){
q.push(tmp);
}
}
}
操作模拟
mb A(mb t){
mb tt;
tt.ans=t.ans+'A';//保留答案数组
for(int i=0;i<8;i++){
tt.xl[i]=t.xl[7-i];
}
return tt;
}
mb B(mb t){
mb tt;
tt.ans=t.ans+'B';
tt.xl[0]=t.xl[3];
tt.xl[1]=t.xl[0];
tt.xl[2]=t.xl[1];
tt.xl[3]=t.xl[2];
tt.xl[4]=t.xl[5];
tt.xl[5]=t.xl[6];
tt.xl[6]=t.xl[7];
tt.xl[7]=t.xl[4];
return tt;
}
mb C(mb t){
mb tt;
tt.ans=t.ans+'C';
for(int i=0;i<8;i++){
tt.xl[i]=t.xl[i];
}
tt.xl[1]=t.xl[6];
tt.xl[2]=t.xl[1];
tt.xl[5]=t.xl[2];
tt.xl[6]=t.xl[5];
return tt;
}
相信读者能够读明白ABC三种操作是怎么实现的,笔者在这不再赘述
康托展开
unordered_set<int> ss;
bool vis(mb t){
int l;
sscanf(t.xl,"%d",&l);//把字符串转换成整数
if(!ss.count(l)){//判断在ss中有没有存过l
ss.emplace(l);//在里面添加一个l
return true;
}
return false;
}
bfs
void bfs(){
while(!q.empty()){
mb now=q.front();//取队头
q.pop();
if(check(now)){//如果找到答案
cout<<now.ans.size()<<endl<<now.ans;
return;
}
mb tmp=A(now);//进行ABC操作
if(vis(tmp)){
q.push(tmp);
}
tmp=B(now);
if(vis(tmp)){
q.push(tmp);
}
tmp=C(now);
if(vis(tmp)){
q.push(tmp);
}
}
}
end
总的来说,这是一道经典的广搜题
需要用到的知识是Hash判重
但是我们直接用特别优美、特别完美的康托展开逃课
相信读者能够读懂主函数部分,笔者在这里不多赘述