GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版
一个立方体只有三个颜色,因此用三个面的位置就可以表示单个立方目前的位置。
每个立方体初始的位置是固定的,变化的是空出来的位置。但是目标只是要求顶面的颜色,没有要求立方体的确切位置。
每次转动时,颜色变化是有规律的,按照方向的不同会变化,这个规律在stepChange中表示。
一开始我使用迭代加深搜索方法去解决。结果正确,但一直超时。我看网上其它人有使用这个方法可以AC的,但我这里就不行(剪枝思路是一样的),可能是它们代码具体操作优化的比较好。
我又看其它方法,我看有人用的是DFS+剪枝,想到使用迭代加深搜索,到第n次遍历时,会与n-1次走的路,计算过程是相同的,因此迭代加深搜索实际上只有当前最后一层是有用的(但迭代加深搜索可以做出来的题目中,很多也确实是这样)。而且这个题目限制了最长步数30次,因此改成DFS试了一下,结果可以AC了。但是也接近3秒,属于比较极限了。
AC代码
cpp
#include <stdio.h>
// 目标状态
int goal[3][3];
// 当前空白位置
int emptyPos[2];
// 当前状态
// stateArr[i][j]为一个立方体的状态 最后一个下标的值表示顶面/上面/右侧面三个颜色值
int stateArr[3][3][3];
int minStep = 31;
// 四个方向移动
int steps[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 四个方向的反向 改回去使用
int stepsRe[4] = {1, 0, 3, 2};
// 四个方向的原值如何覆盖为新值的对应关系
int stepChange[4][3] = {
{1, 0, 2},
{1, 0, 2},
{2, 1, 0},
{2, 1, 0}};
// 判断当前状态是否符合终点状态 0表示符合
int judge()
{
int n = 0;
int i, j;
for (i = 0; i < 3; ++i)
{
for (j = 0; j < 3; ++j)
{
if (goal[i][j] != stateArr[i][j][0])
return ++n;
}
}
return n;
}
void dfs(int step, int preStep)
{
if (step >= minStep)
return;
int jnum = judge();
if (jnum == 0)
{
minStep = step;
return;
}
// step + 1 + jnum - 1
if (step + jnum > 30)
return;
int preA = emptyPos[0], preB = emptyPos[1];
int i, j, k, a, b;
// 四个方向遍历
for (i = 0; i < 4; ++i)
{
if (stepsRe[i] == preStep)
continue;
// 正向旋转过去
// a,b 要转的位置
a = steps[i][0] + preA;
b = steps[i][1] + preB;
if (a < 0 || a > 2 || b < 0 || b > 2)
continue;
for (j = 0; j < 3; ++j)
stateArr[preA][preB][j] = stateArr[a][b][stepChange[i][j]];
emptyPos[0] = a;
emptyPos[1] = b;
stateArr[a][b][0] = 0;
dfs(step + 1, i);
// 反向旋转回来
for (j = 0; j < 3; ++j)
stateArr[a][b][j] = stateArr[preA][preB][stepChange[stepsRe[i]][j]];
emptyPos[0] = preA;
emptyPos[1] = preB;
stateArr[preA][preB][0] = 0;
}
}
int main()
{
int i, j, k, jnum;
char c;
while (scanf("%d %d", &emptyPos[1], &emptyPos[0]) == 2)
{
minStep = 1000;
if (emptyPos[0] == 0 || emptyPos[1] == 0)
return 0;
emptyPos[0]--;
emptyPos[1]--;
// 读入换行符
getchar();
for (i = 0; i < 3; ++i)
{
for (j = 0; j < 3; ++j)
{
scanf("%c", &c);
// 白色1 蓝色2 红色3 空0
switch (c)
{
case 'W':
k = 1;
break;
case 'B':
k = 2;
break;
case 'R':
k = 3;
break;
case 'E':
k = 0;
break;
}
goal[i][j] = k;
// 读入一个分隔符
getchar();
}
}
// 初始化当前状态
for (i = 0; i < 3; ++i)
{
for (j = 0; j < 3; ++j)
{
stateArr[i][j][0] = 1;
stateArr[i][j][1] = 3;
stateArr[i][j][2] = 2;
}
}
stateArr[emptyPos[0]][emptyPos[1]][0] = 0;
dfs(0, -1);
if (minStep > 30)
printf("-1\n");
else
printf("%d\n", minStep);
}
return 0;
}