题目大意:
对于一个2*4的魔板,你有三种操作:
A 交换上下两行
B 将最右边的一列插入最左边
C 魔板中央四格作顺时针旋转
已知
1234
8765
为魔板复原的样子,现在给出一个魔板,要求输出复原的最少步数以及依次进行的操作。
思路
这道题我听地真的很懵。。。
正解是BFS+HASH,从复原的样子开始搜索,把三种变化方法都搜一遍,直到成为了输入的样子(此时的方案绝对是最优方案),然后递归输出。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int k=40319;
int c[4][9],sum,x,father[k],ha[k],way[k],head,state[k][9],tail;
char a[k][9],b[k];
void csh() //初始化
{
c[1][1]=8;
c[1][2]=7;
c[1][3]=6;
c[1][4]=5;
c[1][5]=4;
c[1][6]=3;
c[1][7]=2;
c[1][8]=1;
c[2][1]=4;
c[2][2]=1;
c[2][3]=2;
c[2][4]=3;
c[2][5]=6;
c[2][6]=7;
c[2][7]=8;
c[2][8]=5;
c[3][1]=1;
c[3][2]=7;
c[3][3]=2;
c[3][4]=4;
c[3][5]=5;
c[3][6]=3;
c[3][7]=6;
c[3][8]=8; //所有变化情况
}
int h(int x) //哈希函数
{
return x%k;
}
int locate(int x) //查找
{
int m=0;
for (int i=1;i<=8;i++)
m=m*10+state[x][i]; //取出该情况
int t=h(m); //求出储存位置
int i=0;
while (i<k&&ha[(i+t)%k]!=m&&ha[(i+t)%k]!=0) i++; //找到一个符合的情况才退出
if (ha[(i+t)%k]==m) return 1; //如果查找到(即这种情况已经用更少步的方法得到就返回1)
else
{
ha[(i+t)%k]=m; //储存
return 0; //返回0
}
}
void print(int x) //递归输出
{
if (x==1) return; //已经无法递归的话就返回
sum++; //计数
if (way[x]==1) b[sum]='A';
if (way[x]==2) b[sum]='B';
if (way[x]==3) b[sum]='C'; //判断
print(father[x]); //递归
}
void bfs()
{
head=0;
tail=1;
father[1]=0;
way[1]=0;
for (int i=1;i<=8;i++) state[1][i]=i; //初始化
do
{
head++;
for (int i=1;i<=3;i++) //枚举每种变化
{
tail++;
way[tail]=i; //记录路径
father[tail]=head; //记录父节点
for (int j=1;j<=8;j++)
state[tail][j]=state[head][c[i][j]]; //更改魔板情况
tail-=locate(tail); //等于 if (locate(tail)==1) tail--;
int ok=0;
for (int i=1;i<=8;i++) //判断是否已经完成
if (state[tail][i]!=state[0][i])
{
ok=1;
break;
}
if (ok==0)
{
print(tail); //输出
tail=-1;
return;
}
}
}
while (head<tail);
}
int main()
{
csh();
int o=0;
for (int i=1;i<=8;i++)
{
scanf("%d",&state[0][i]);
o=o*10+state[0][i];
}
if (o==12345678) //特殊判断
{
printf("0\n\n");
return 0;
}
bfs();
printf("%d\n",sum);
for (int i=sum;i>=1;i--)
{
putchar(b[i]);
if (i%60==0) printf("\n");
}
return 0;
}