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;
}
相关推荐
Yzzz-F2 分钟前
2018-2019 ACM-ICPC, Asia Dhaka Regional ContestC[数论]
算法
Frostnova丶8 分钟前
LeetCode 3474. 字典序最小的生成字符串
算法·leetcode·职场和发展
REDcker8 分钟前
Nagle 算法与 TCP_NODELAY、TCP_CORK 详解
网络·tcp/ip·算法
β添砖java24 分钟前
深度优先搜索DFS
算法·深度优先
小糯米60127 分钟前
C++ 并查集
java·c++·算法
IronMurphy27 分钟前
【算法三十四】39. 组合总和
算法·深度优先
重庆小透明27 分钟前
力扣刷题【3】相交链表
算法·leetcode·链表
算法鑫探27 分钟前
C语言实战:学生成绩统计与分析
c语言·数据结构·算法·新人首发
IAUTOMOBILE30 分钟前
Code Marathon 项目源码解析与技术实践
java·前端·算法
Lyyaoo.30 分钟前
【JAVA基础面经】深拷贝与浅拷贝
java·开发语言·算法