有数字的最短路径

Shortest path with figures on board

本文关键字:最短路径 数字      更新时间:2023-10-16

我在做家庭作业时遇到问题(C++)。我不是在要求完整的解决方案,但朝着正确的方向倾斜可能会有所帮助。:)

我有一个NxN板(最大N = 100)和该板上的1x2数字(立方体)。立方体的一侧涂成红色,另一侧涂成蓝色。立方体的默认位置是棋盘的左上角,蓝色朝上:

B B . .
. . . .
. . . .
. . . .

(4x4 示例,B 代表蓝色)

黑板上可能有石头(障碍物)。我可以用我的身材做出的动作:

  • 顺时针旋转 90/180/270 度
  • 您可以围绕立方体
  • 的右/左/上/下边缘翻转立方体,更改其"向上颜色"

例如,在默认位置使用右翻转:

. . R R
. . . .
. . . .
. . . .

然后使用旋转 90:

. . R .
. . R .
. . . .
. . . .

然后使用左翻转:

. B . .
. B . .
. . . .
. . . .

当然,旋转或翻转时,您不能落在石头上。所以,问题是 - 对于电路板的任何给定配置(图形位置和石头位置),编写一个程序,该程序将在默认位置"将立方体带回家"(蓝色面朝上!使用最少的移动次数,如果可能,则返回 1,如果不可能,则返回 0。

我觉得这个问题很有趣,但我不得不承认我对它有点困惑。尤其是蓝色侧面/红色侧面部分。我真的不知道如何"翻译"那些可以用通常的最短路径算法语言使用的动作(我从来没有使用过这些)。因此,如果您能给出的每一条建议,我将不胜感激!:)

首先,由于要求您找到确切的最佳路径,我将使用Dijksta的算法。

对于此算法,您需要:

  1. 一个给出下一个的函数可能的移动。
  2. 告诉您仓位是否已经存在的函数访问。
  3. 告诉您每条路径的总成本的函数。
  4. 当然还有一个功能,告诉你什么时候到达决赛位置

给定初始位置,您的立方体可以恰好达到 7 个新位置。很容易选择哪些是可能的。

G 只是您到目前为止所做的移动次数 + 1 表示下一步:)

我会使用哈希表来跟踪访问的位置。(这可能是最难写的函数),但你现在不需要过度思考。一个简单的向量和逐项比较就可以了。您可以在代码运行后对其进行优化。

最后,您需要检查立方体是否处于其初始位置,蓝色面朝上。

您可以将每个可能的 1x2 块和颜色(红色或蓝色)组合解释为顶点并作为边移动。如果有可能在一次移动中从其他组合到达特定的 1x2 块和颜色组合(顶点),那么这两个组合之间存在连接(边缘)。然后,您必须在结果图表中找到给定配置和"主页"配置之间的最短路径(可能是广度优先搜索,因为无论您执行什么移动,移动的成本都是相同的)。

如果想走得更远,您可以使用高级图形搜索算法,该算法在图形遍历期间使用启发式(启发式是到达目的地所需的最小移动量,假设黑板上没有障碍物)。例如,您可以使用 A* 算法。

在处理此类问题时,首先要做的是找到问题状态的表示。在这种情况下,您需要:

  1. 两个整数,代表图形的左上角位置
  2. 一个表示图形颜色的布尔值(红色/蓝色)
  3. 一个表示图形方向的布尔值(水平/垂直)

如果您熟悉位掩码,则应仅使用 32 位整数来执行此操作(8 位表示 x 位置,8 位表示 y 位置,2 位用于其余位置)。这样,您就不需要实现比较运算符。你用这 3 个信息定义了一个简单的结构(称之为 state ),并对此进行了严格排序的比较(这只需要将state放入std::set中。

在此之后,您可以使用BFS解决此问题。

为此,您需要:

  1. 一种std::map<state,state>,用于在键中存储您已经访问过的位置,以及您在值中来自的位置(如果您可以使用 c++11 并使用位掩码存储您的状态,请将map替换为 unordered_map
  2. 推送和弹出要处理的状态的std::queue<state>
  3. 一些代码来确定从给定状态可到达的每个可能状态(即实现所有可能的移动,照顾电路板尺寸)

伪代码:

   map<state,state>  visited;
   queue<state> to_be_processed;
   visited.insert( initial_state,initial_state); //you are not coming from anywhere
   to_be_processed.push ( initial_state);
   
   while(!to_be_processed.empty()) {
              state cur = to_be_processed.pop();
              if ( cur == end_state) //you are done
              {
                    //to get the path from initial_state to end_state you have just to walk visited in the inverse order.
                    return 1;
              }
              for ( i = every possible state reachable from cur) {
                    if (visited.count(i) != 0) continue; //already visited
                    to_be_processed.push(i);
                    visited.insert(i,cur); //i has been visited, and you reached i from cur
              }
   }
   return 0; //if you get here, no way

障碍物的存在使问题更难编码,但在概念上没有什么不同。

请注意,在这种情况下,BFS 之所以有效,是因为您从一个州到另一个州支付的费用始终相同。