在成功地发明了魔方之后,鲁比克先生发明了它的二维版本,称作魔板。这是一张有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
对于每种可能的状态,这三种基本操作都可以使用。
你要编程计算用最少的基本操作完成基本状态到目标状态的转换,输出基本操作序列。
解题思路:
用康托展开将魔板的状态(如2 6 8 4 5 7 3 1)映射到[0, 8! - 1] = [0, 40319]中,这种映射是一一对应的,作为本题的hash方法来节省空间。并且有将[0, 40319]中的数还原为魔板状态的方法。实现均不复杂,详见维基百科(http://zh.wikipedia.org/wiki/%E5%BA%B7%E6%89%98%E5%B1%95%E5%BC%80)
用Change(int hash_in, int method)模拟A, B, C三种操作。其中hash_in为输入的模板状态经过康托展开后的hash值,method = [0, 2]分别为A, B, C三种操作,而输出值为经过method操作之后魔板状态的hash值。
从初始状态的hash值开始BFS,用hash_table保存BFS的状态。其中hash_table[i][0]代表从初始状态变化到hash值为i的状态所经过的步骤数,hash_table[i][1]则代表状态i的父节点的状态值,hash_table[i][2]代表由i父节点变化到i所使用的操作([0, 2])
BFS结束后,计算输入的目标状态值的hash值goal_hash,从hash_table[goal_hash][0..2]回溯到初始状态值就能够获得从初始状态值到目标状态值的最短路径以及其长度。
代码:
/*
ID: zc.rene1
LANG: C
PROG: msquare
*/
#include
#include
#include
#define MAX 40320
int factorial[8];
int visited[9];
int hash_table[40320][3];
void GenerateFactorial(void)
{
int i;
factorial[0] = 1;
for (i=1; i<8; i++)
{
factorial[i] = i * factorial[i - 1];
}
}
int GetHash(int in[8])
{
int ret = 0;
int i, j, count;
memset(visited, 0, 9*sizeof(int));
for (i=0; i<8; i++)
{
count = 0;
for (j=1; j
{
if (visited[j] == 0)
{
count++;
}
}
visited[in[i]] = 1;
ret += count * factorial[7 - i];
}
return ret;
}
void ReverseHash(int hash_code, int ret[8])
{
int result, remainder, temp;
int i, j;
memset(visited, 0, 9*sizeof(int));
for (i=7; i>=0; i--)
{
remainder = hash_code % factorial[i];
hash_code /= factorial[i];
temp = hash_code + 1;
for (j=0; j<=temp; j++)
{
if (visited[j] == 1)
{
temp++;
}
}
ret[7 - i] = temp;
visited[temp] = 1;
hash_code = remainder;
}
}
int Change(int hash_in, int method)
{
int i, temp;
int in[8];
ReverseHash(hash_in, in);
if (method == 0)
{
for (i=0; i<4; i++)
{
temp = in[i];
in[i] = in[7-i];
in[7-i] = temp;
}
}
else if (method == 1)
{
temp = in[3];
for (i=3; i>0; i--)
{
in[i] = in[i-1];
}
in[0] = temp;
temp = in[4];
for (i=4; i<7; i++)
{
in[i] = in[i+1];
}
in[7] = temp;
}
else
{
temp = in[6];
in[6] = in[5];
in[5] = in[2];
in[2] = in[1];
in[1] = temp;
}
return GetHash(in);
}
void BFS(int hash_code, int level)
{
int i, j, k;
int method, hash_change;
int queue[40320];
memset(queue, -1, 40320*sizeof(int));
memset(hash_table, -1, 40320*3*sizeof(int));
i = 0;
j = 0;
k = 0;
queue[k++] = hash_code;
j++;
hash_table[hash_code][0] = level;
while (i != j)
{
level++;
for (i=0; i
{
hash_code = queue[i];
for (method=0; method<3; method++)
{
hash_change = Change(hash_code, method);
if (hash_table[hash_change][0] == -1)
{
queue[k++] = hash_change;
hash_table[hash_change][0] = level;
hash_table[hash_change][1] = hash_code;
hash_table[hash_change][2] = method;
}
}
}
j = k;
}
}
int main(void)
{
FILE *fin, *fout;
int i;
int goal[8];
int init[8] = {1, 2, 3, 4, 5, 6, 7, 8};
int goal_hash, len;
int *path = NULL;
fin = fopen("msquare.in", "r");
fout = fopen("msquare.out", "w");
for (i=0; i<8; i++)
{
fscanf(fin, "%d", &goal[i]);
}
GenerateFactorial();
BFS(GetHash(init), 0);
goal_hash = GetHash(goal);
len = hash_table[goal_hash][0];
path = (int *)malloc(len*sizeof(int));
for (i=0; i
{
path[len-1-i] = hash_table[goal_hash][2];
goal_hash = hash_table[goal_hash][1];
}
fprintf(fout, "%d\n", len);
for (i=0; i
{
fprintf(fout, "%c", path[i] + 'A');
}
fprintf(fout, "\n");
return 0;
}