UVA-1604 立体八数码问题 题解答案代码 算法竞赛入门经典第二版

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;
}
相关推荐
m0_531237172 小时前
C语言-while循环,continue/break,getchar()/putchar()
java·c语言·算法
一条大祥脚2 小时前
KMP原理+例题
算法
追随者永远是胜利者12 小时前
(LeetCode-Hot100)20. 有效的括号
java·算法·leetcode·职场和发展·go
瓦特what?12 小时前
快 速 排 序
数据结构·算法·排序算法
niuniudengdeng12 小时前
基于时序上下文编码的端到端无文本依赖语音分词模型
人工智能·数学·算法·概率论
hetao173383712 小时前
2026-02-13~16 hetao1733837 的刷题记录
c++·算法
你的冰西瓜15 小时前
2026春晚魔术揭秘——变魔法为物理
算法
忘梓.15 小时前
解锁动态规划的奥秘:从零到精通的创新思维解析(10)
c++·算法·动态规划·代理模式
foolish..15 小时前
动态规划笔记
笔记·算法·动态规划