题目描述
在成功地发明了魔方之后,拉比克先生发明了它的二维版本,称作魔板。这是一张有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个字符。
样例输入
2 6 8 4 5 7 3 1 |
样例输出
7 BCABCCB |
分析
看到题目,应该是很容易想到广搜的,搜索主体程序也比较容易写,就是如何判重,这是本题的一个难点。我们考虑将每一种状态转化为一个8位十进制数,可这样最大的一种(87654321)也有可能会爆内存。实际上总共的状态数量也只有8!=40320(x!为x的阶乘,即1*2*3*...*(x-1)*x)个,可我们为什么要开到87654321这么大的数组呢?这是因为这其中有很多不可能会出现发情况,比如66666666,这就造成了空间浪费。因此,考虑将每一种状态对应其全排列的序数,这样就只需要开40320的数组。怎么做到呢?这里可以用康托展开,也可以用另一种将排列转序数的方法,当然,如果不嫌时间慢,暴力枚举也可以。其实,这样也是一种hash判重的算法。
代码
#include <iostream>
#include <cmath>
using namespace std;
int now=12345678;//定义初始状态
int goal;//目标状态
int book[40325];//判重用数组
struct node {
int s,f,data;
char way;
}que[40325];
int f(int a) {//hash函数
int arr[9];
for (int i=int(pow(10,7)),j=1;i>=1;i/=10,j++) {//将数字转化为数组
arr[j]=a/i;
a%=i;
}
int num=0,k=1;
for (int i=7;i>=1;i--) {
for (int j=i+1;j<=8;j++) {
if (arr[j]<arr[i]) num+=k;
}
k*=9-i;
}
return num+1;
}
int actionA(int a) {//三种操作
int b=0;
for (int i=int(pow(10,7)),j=1;i>=1;i/=10,j*=10) {
int p=a/i;
b+=p*j;
a%=i;
}
return b;
}
int actionB(int a) {
int arr[9];
for (int i=int(pow(10,7)),j=1;i>=1;i/=10,j++) {
arr[j]=a/i;
a%=i;
}
arr[0]=arr[4];
for (int i=4;i>=2;i--) arr[i]=arr[i-1];
arr[1]=arr[0];
arr[0]=arr[5];
for (int i=5;i<=8;i++) arr[i]=arr[i+1];
arr[8]=arr[0];
a=0;
for (int i=int(pow(10,7)),j=1;i>=1;i/=10,j++)
a+=arr[j]*i;
return a;
}
int actionC(int a) {
int arr[9];
for (int i=int(pow(10,7)),j=1;i>=1;i/=10,j++) {
arr[j]=a/i;
a%=i;
}
swap(arr[6],arr[7]);
swap(arr[3],arr[6]);
swap(arr[2],arr[3]);
a=0;
for (int i = int(pow(10,7)),j=1;i>=1;i/=10,j++)
a+=arr[j]*i;
return a;
}
void print(int k) {//递归输出路径
if (que[k].f) print(que[k].f);
if (que[k].way != 0) cout<<que[k].way;
}
int main()
{
for (int i=1;i<=8;i++) {
int _n;
cin>>_n;
goal+=_n*int(pow(10,8-i));
}
if (goal==now) {
cout<<0<<endl;
return 0;
}
int head,tail;//广搜主体
head=tail=1;
que[tail].data=now;
que[tail].f=0;
que[tail].s=0;
book[f(now)]=1;
tail++;
while (head<tail) {
for (char k='A';k<='C';k++) {
int dt;
switch(k) {
case 'A':
dt=actionA(que[head].data);
que[tail].way='A';
break;
case 'B':
dt=actionB(que[head].data);
que[tail].way='B';
break;
case 'C':
dt=actionC(que[head].data);
que[tail].way='C';
break;
}
if (book[f(dt)]) continue;
book[f(dt)]=1;
que[tail].data=dt;
que[tail].s=que[head].s+1;
que[tail].f=head;
if (dt==goal) {
cout<<que[tail].s<<endl;
print(tail);
return 0;
}
tail++;
}
head++;
}
return 0;
}