以最少的动作同时解决所有4x4迷宫

时间:2014-11-13 13:48:58

标签: algorithm path-finding maze

我遇到了这个非常有趣的问题,我们有一个4x4的迷宫和一个机器人试图进入目标。问题是,您必须找到一系列预定义命令,这些命令将始终导致机器人达到目标。

我们假设我们有一个像这样的迷宫:

x . . .
. # # .
. # # .
. . . g

这个特殊的迷宫可以用例如命令序列DDDRRRRRRDDD来解决,其中R =右,L =左,U =上,D =下(duh)。 / p>

然而,这些序列都不会解决这个迷宫:

x . # .
. . . .
# . . .
. . . g

机器人始终从左上角开始,目标始终位于右下角,迷宫始终是2D 4x4矩阵。

我已经实现了一个算法,它让我获得了78个命令的获胜序列。我确信至少存在29个命令的解决方案(其他人完成了这个)。

这个问题实际上已经存在了几年,所以我丢失了当时使用的算法,但基本的想法是在我生成的所有迷宫中搜索,并始终选择路径结果解决了最多的迷宫。这实际上让我得到了一个略长于78的序列;我手动减少了一些命令,我​​发现这些命令是多余的。

是的,暴力迫使将需要多年的时间。

如果我的记忆服务,可能会有少于4000个迷宫(可能的迷宫是存在左上角和右下角之间的路径)。

OH!机器人在执行命令期间至少一次访问目标就足够了。也就是说,在最后一个命令之后,它不必坐在目标上。

我是否抓住了任何人的兴趣?我应该如何处理这个问题以获得更有效的答案?感谢您考虑:)


有趣的事情:Pastebin

它是(非常)匆忙拼凑出一块Java。它应该编译运行:) 该程序有点同时播放~4000个迷宫。程序对UP,LEFT,DOWN和RIGHT进行输入(w,a,s,d),然后模拟移动,显示一些统计数据。如果你尝试的话,你可以在屏幕上看到的是每个位置每个迷宫中的障碍物总数,以及每个迷宫的当前位置的总量。很难解释:)问我是否有问题。

再次......不要介意可怕的代码。它写于20分钟......。


进度

我间接地从this user's answer得到了这个想法,并在聊天中用Mooing Duck进一步建模。我们的想法是找到一个解决迷宫右侧的序列。也就是说,解决所有迷宫中至少一半的解决方案,以及镜像并从头开始再次运行解决剩余的迷宫。

插图:

首先找到一个序列,其第一个命令是RIGHT,它解决了这个迷宫:

0 1 0 0
0 1 0 0
0 0 0 0
0 1 0 0

一个这样的序列是RDDRRRD。该序列的镜像对应物是

R -> D
D -> R
L -> U
U -> L

这意味着RDDRRRD - > DRRDDDR

现在,这个镜像序列能否解决迷宫?不,它卡住了。因此,即使对于这个迷宫,它也不是有效的序列。我们必须找到这样一个序列,它至少可以解决所有迷宫的一半,并且它的镜像对应物在从一开始就再次运行时解决了其余部分。

在简单地强制执行R,D和L的所有可能排列之后,我得到了一些可能的序列。

一个这样的序列是RRDRRRDRLDRDR

现在接下来的问题是,在运行此序列之后,剩余的迷宫处于随机混乱状态。我们需要获得最短(最佳)可能的序列,使所有剩余的迷宫回到起始位置(0,0)。这部分我只是手工完成(现在)。我的答案绝不是最佳选择,但它让所有的迷宫都回到了开头。

此序列为LDLUULURUUULULL

在此之后我们只需运行镜像序列DDRDDDRDURDRD,我们就解决了所有的迷宫问题。

这个特殊的顺序是完整的:

RRDRRRDRLDRDRLDLUULURUUULULLDDRDDDRDURDRD - 41次移动

虽然这是一个充满希望和令人瞩目的里程碑,但它仍然远离最佳证明的解决方案。非常欢迎任何见解!还要感谢迄今为止帮助过我的每个人:)

序列缩小

我已经无法以编程方式获得比58移动长序列更好的答案。然而,通过上述方法并手工磨制字符,我已经能够将序列缩小到仅33个字符。这个顺序如下:

RRDRRDRLDRDLDLULLLDDRDDRDRURRRDDR - 33次移动

虽然序列现在非常接近29目标,但我仍然在寻找具有相同口径的程序化获取解决方案。从序列中删除字符时我没有使用逻辑 - 我只是删除了一个字符并检查它是否解决了所有迷宫,冲洗并重复。

10 个答案:

答案 0 :(得分:10)

我将此问题编码为一个SAT问题,其中包含4280308变量和21975717子句(包括许多冗余但貌似有用的子句),treengeling在大约100 1/2小时后解决了该问题,找到了解决方案长度为29的字符串:

RRDRRDRDLDLDULLDLDRDDURDRRDRR

在将近85小时后得出的类似计算结果是,不存在长度为28的解

这是我用来创建SAT问题的快速而肮脏的Haskell程序:

import Data.List(tails,nub)
import Data.Bits
import Numeric(showHex)
import System.Environment(getArgs)

data Lit = Lit Bool [Char]

type Wall = Int -- [Bool]

lit s = Lit True s
litS s = lit (s "")

inv (Lit b s) = Lit (not b) s

instance (Show Lit) where
  showsPrec _ (Lit b s) = showString (if b then "" else "~") . showString s
  showList = showString . unwords . (map show)

showDir d = showChar ("NESW"!!d)
dir n d = litS $ showChar 'D' . shows n . showDir d

showStuff n s p = showHex n . showChar (['A'..]!!p) . shows s
pos n s p = litS $ showChar 'P' . showStuff n s p
posh n s p h = litS $ showDir h . showStuff n s p

opdir :: Int -> Int
opdir d = (d+2) `mod` 4

(<-&) :: Lit -> [Lit] -> [[Lit]]
l <-& ls = lt : lf where 
  lt = l : map inv ls                   --      l or ~l1 or ~l2 ...
  lf = [ [ inv l, li ] | li <- ls ]     --      ~l or li , all i

(<-|) :: Lit -> [Lit] -> [[Lit]]
l <-| ls = lf : lt where 
  lf = (inv l) : ls                     --      ~l or l1 or l2 ...
  lt = [ [ l, inv li ] | li <- ls ]     --      l or ~li , all i

atmostone l = [ [inv a, inv b] | (a:bs) <- tails l, b <- bs ]

dirconds n = concat [ atmostone [ dir i d | d <- [0..3]]
                    | i <- [0..n-1] ]

boundary p = (p<5) || (p>24) || (p `mod` 5 == 0)
positions = [ p | p<-[0..24], not (boundary p) ]
start = head positions
stop = last positions
wp = [ if boundary p then 0 else p - 4 - p `div` 5 | p <- [0..23]]
       ++ [1,0,0,0,0,0]

wallat :: Wall -> Int -> Bool
wallat w p = testBit (4*w+1) (wp!!p) -- (True:False:w) !! (wp!!p)

jump:: Int -> Int -> Int
jump pos dir =  pos + ([-5,1,5,-1]!!dir)

freestep :: Wall -> Int -> Int -> Maybe Int
freestep w pos dir = let np = jump pos dir in 
           if wallat w np
              then Nothing
              else Just np

reach :: Wall -> Int -> [Int]
reach w p = [ np | Just np <- map (freestep w p) [0..3] ]

reachable :: Wall -> [Int]
reachable w = go [start] [start] where
                 go seen [] = seen
                 go seen front = let new = nub [ n | p <- front,
                                                     n <- reach w p,
                                                     n `notElem` seen ]
                                 in go (seen++new) new

nicereachable :: Wall -> Maybe [Int]
nicereachable w =
  let r = reachable w
  in if and [ p `elem` r || wallat w p | p <- positions]
       then Just r
       else Nothing

mazestepdirposconds w n p d =
  let ph = posh w (n+1) p d
      conds = case freestep w p d of
                (Just np) -> ph <-& [ pos w n np, dir n (opdir d) ]
                Nothing   -> ph <-& [ pos w n p, dir n d ]
  in (ph,conds)

mazestepposconds w n p =
  let cnds = map (mazestepdirposconds w n p) [0..3]
      ps = pos w (n+1) p
  in ( ps <-| (map fst cnds)) ++ (concatMap snd cnds)

mazestepconds w r n = concatMap (mazestepposconds w n) r

mazeconds w len r = [ pos w 0 start ] :
                    [ pos w i stop | i <- [6..len] ] :
                    (concat [ atmostone [ pos w s p | p <- positions ] 
                                          | s<-[0..len] ]) ++
                    (concatMap (mazestepconds w r) [0..len-1])

conds l = dirconds l ++ 
          concat [ mazeconds w l r |
                   (w,Just r) <- [(i,nicereachable i)|i<-[0..2^14-1]]]

main = do
         [n] <- getArgs
         mapM_ print $ conds (read n)

它带有一个命令行参数,一个整数,表示解决方案字符串的长度。输出是CNF SAT问题,每行有一个子句,符号文字和反斜线号。这是Donald Knuth用于SAT解算器here的格式。我使用他的SAT-TO-DIMACS程序将其转换为更常用的DIMACS格式。它是用CWEB编写的,因此必须使用ctangle将其转换为C程序。您还需要gb_flip.w。该程序从stdin读取并写入stdout,您将希望为它提供一个类似h20的选项,以增加其哈希表的大小。

为打破对称性,我添加了单位子句D0E,以强制第一步顺利进行。 (请注意,我以前使用NESW而不是URDL,因为我以前曾使用a similar problem作为指导来阅读这些内容。)

程序会考虑所有2423个迷宫,每个位置都是 可达或墙壁。就像@Hans_Olsson observed, 仅需足够 考虑2083个迷宫,每个位置都是墙或可到达的位置 没有通过目标。要优化程序只考虑这些 迷宫,在p /= stop,的定义中,在p <- front,之后添加reachable

编码

(我将添加与该程序的描述有关的说明。 如果您只对编码感兴趣,可以忽略它们。)

len为我们正在寻找的解决方案的长度, 令i为范围0<=i<=len的整数(除非另有说明)。 令m遍及所有考虑的迷宫,令p遍及所有迷宫 特定迷宫的可达位置。 可达位置包括值startstop 开始位置和目标。 让d跨越四个可能的方向。

(程序将m输出为十六进制的14位数字,对墙壁进行编码 位置,p为大写字母。它使用变量名 不一致:n代表milenw(墙壁)代表ms(步骤)代表i,在某些情况下,h(助手)代表d。)

对于每个i<len和每个d,都有一个变量D<i><d>指示 解决方案的第i步是朝方向d前进。 (程序使用dir函数创建它们。)

对于每个i0<len,都有一些子句要求最多 四个变量D<i0><d>是正确的。

对于每个mip,都有一个变量P<m><i><p>表示 在迷宫m中,在时间i到达位置p。 (程序使用pos函数创建它们。)

对于每个迷宫m0,都有一个单位子句P<m0><0><start>建立 起始位置。

对于每个m0i0,都有一些子句要求: 变量P<m0><i0><p>为真(我们不能处于两个不同的位置)。 除了i0=0(可以替换的情况)之外,这些都是多余的 对于所有~P<m0><0><p>的单位条款p!=start),但似乎有帮助。

根据时间从i0到时间i0+1的迷宫的进展 D<i0><d>中给出的方向是使用辅助变量来描述的。 对于每个mi>0pd,都有变量 P<m><i><p><d>。 (程序使用posh函数创建它们。将它们打印为 <d><m><i><p>,以便将变量名的长度保持在 Knuth的程序规定了8个字符。)

这个想法是每个方向都给出一个位置可能的原因 可能达到。该变量表示 在迷宫m中,在时间i处位置p是“由于” d引起的。 如果我们考虑朝某个方向前进,撞墙然后从 它来自那个方向,那么我们可以解释变量 就是从d方向到达那个位置。

因此,让我们考虑一些固定的mi<lenpdP<m><i+1><p>为何由于d而成立? 如果没有d方向的墙壁(来自p), 那么我们可能是从那里来的;我们将该位置称为np。 如果有一堵墙,那么我们可能已经 在此之前,试图去那里撞墙。

因此,我们需要建立以下条款 P<m><i+1><p><d>等价于 P<m><i><p'>D<i><d'>,其中p'=npd'是相反的方向 如果没有墙,则d的值;如果有墙,则p'=pd'=d的值。 (程序在函数mazestepdirposconds中执行此操作。)

然后,我们只需要建立用于为每个m0i0>0p0的子句, 变量P<m0><i0><p0>等于析取(逻辑或) 四个变量P<m0><i0><p0><d>中。

最后,我们需要添加解决迷宫的条件。 因此,对于每个迷宫m0,我们需要一个子句,要求其中一个变量 P<m0><i><stop>是正确的。由于迷宫无法在6步之内解决, 我们只需要考虑i>=6

答案 1 :(得分:4)

使用meta A *算法和C#,我发现了以下32和31个字符序列(到目前为止):

RRDRRDRLDRDLDLULLLDDRDDRDRURRDRD (32 characters)
RRDRRDRLDRDLDLULLLDDRDDRDURRDRRD (32 characters)
RRDRRDRLDRDLDLULLLDDRDDDURDRRDR (31 characters)

forked Olavi's ideone使用31个字符序列以确保我没有犯错。

关于迷宫计数,我使用洪水填充算法获得3828个有效迷宫。

项目源代码和编译版本二进制文件(在bin \ release文件夹中)Google Drive

您可以在那里输入A *搜索的起始字符串和最大搜索长度。 对代码进行了相当多的速度优化,但仍有更多优势。例如,对于每个扩展,它创建4个Candidate类的实例,创建新的迷宫,运行旧候选的每个移动,然后是4个不同的方向(左,右,上,下)。使用Candidate.Clone()方法,可以大大提高性能,分析器可以显示当前的热点。此外,到目前为止还没有多线程,程序使用越来越多的内存用于访问列表(在我的PC上10分钟后大约2 GB)。请注意,即使在发布模式下,在IDE中运行程序也会使其速度降低,因此最好从外部启动它。

导致上述序列的基本元算法是:

  • A *搜索长度为N的已知字符串,其最大长度为M,从末尾删除越来越多的字符,例如

    A *搜索RRDRRDRLDRDLDLULLLDDRDDRDRURRRDD(32个字符),M = 33

    A *搜索RRDRRDRLDRDLDLULLLDDRDDRDRURRRD(31个字符),M = 33

    A *搜索RRDRRDRLDRDLDLULLLDDRDDRDRURRR(30个字符),M = 33

    A *搜索RRDRRDRLDRDLDLULLLDDRDDRDRURR(29个字符),M = 33

    ...

  • 一旦找到比N更短的字符串,将其用作A *搜索的新最大长度,以使其更快并占用更少的内存。

我尝试过的实际组合可以在源代码中看到,请参阅下面的代码段。时间来自较旧的未经优化的版本,当前版本应该快6倍左右。

        //// 33 char solution
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRURRRDDR", 33); // 33 chars, 00:00:00.0032911 Finished, 1 solution, best 33, visitedList length 0

        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRURRRDD", 33); // 32 chars, 00:00:00.0308543 Finished, 1 solution, best 33, visitedList length 3
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRURRRD", 33); // 31 chars, 00:00:00.0871429  Finished, 2 solutions, best 33, visitedList length 14
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRURRR", 33); // 30 chars, 00:00:00.2536057  Finished, 2 solutions, best 33, visitedList length 49

        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRURR", 33); // 29 chars, 00:00:01.0540762  Finished, 8 solutions, best 32, visitedList length 205
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRUR", 33); // 28 chars, 00:00:03.8993877  Finished, 7 solutions, best 32, visitedList length 771
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRU", 33); // 27 chars, 00:00:10.4225150  Finished, 7 solutions, best 32, visitedList length 2069
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDR", 33); // 26 chars, 00:00:24.2552908  Finished, 7 solutions, best 32 visitedList length 4484
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRD", 33); // 25 chars, 00:01:44.3295165  Finished, 14 solutions, best 32, visitedList length 16600
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDR", 33); // 24 chars, 00:16:18.6666045  Finished, 14 solutions, best 32, visitedList length 77106

        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRURR", 32); // 29 chars, 00:00:00.3134699  Finished, 1 solution, best 32, visitedList length 66
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRUR", 32); // 28 chars, 00:00:01.1053798  Finished, 1 solution, best 32, visitedList length 238
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDRU", 32); // 27 chars, 00:00:03.5172143  Finished, 1 solution, best 32, visitedList length 730
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRDR", 32); // 26 chars, 00:00:07.1336796  Finished, 1 solution, best 32, visitedList length 1413
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDRD", 32); // 25 chars, 00:00:26.4906874  Finished, 2 solutions, best 32, visitedList length 5084
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDDR", 32); // 24 chars, 00:02:52.8134463  Finished, 2 solutions, best 32, visitedList length 24623
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDD", 32); // 23 chars, cancelled after 6 seconds, finds RRDRRDRLDRDLDLULLLDDRDDDURDRRDR (31 chars)

        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRDD", 31); // 23 chars, 00:01:58.4861802  Finished, 1 solution, best 31, visitedList length 18835
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDRD", 30); // 22 chars, 00:00:34.6602434  Finished, 0 solution, best distance 44, visitedList length 21084  
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDDR", 30); // 21 chars, 00:04:32.2439241  Finished, 0 solution, best distance 44, visitedList length 78500
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLDD", 30); // 20 chars, cancelled after 10 minutes, found no solution, best distance 44
        //var aStarSearch = new AStarSearch("RRDRRDRLDRDLDLULLLD", 30); // 19 chars, cancelled after 10 minutes, found no solution, best distance 44

        //var aStarSearch = new AStarSearch("R", 29); // Complete search, would take waaay too long and consume much memory 

答案 2 :(得分:3)

听起来你可以在这里使用A *搜索,将所有迷宫中的最大启发式作为启发式。保守地接近解决方案的距离,可能会给出合理的第一种方法。

由于所有迷宫都很小,你可以通过从每个迷宫的末尾反向运行BFS来预先计算从每个点到每个迷宫的目标的距离,从而为每个迷宫构建一个完美的启发式算法。如果你在查找表中缓存了这个,你可以使用一个迷宫启发式方法,完美地告诉你剩下的最小移动次数。

我还没有尝试过这个,所以这仍然需要通过实验验证,但我认为这将是解决方案的一个很好的起点。

编辑我刚刚阅读了说明每个机器人必须至少一次访问目标并且不一定以目标结束的说明。在这种情况下,将启发式修改为距离目标尚未达到目标的任何机器人的最大距离。

希望这有帮助!

答案 3 :(得分:3)

一些想法:

  • 任何子串RLR,LRL,UDU或DUD都不能出现在任何最佳解决方案中,因为在每个迷宫中,它们将机器人留在同一位置(如果第一次移动被墙壁阻挡或沿第一次移动方向的一步(否则),这在每种情况下与仅执行序列中的第一次移动的结果相同。对于RRLLRR,LLRRLL等也是如此(可能对于所有更长的模式,尽管我还没有验证过,并且它们在修剪搜索方面会产生递减的回报)。 [编辑:这不太对 - 仅当尚未到达g的机器人在三个动作的第二个中达到g时才适用。棘手!]
  • 在任何有效的解决方案中,如果交换了所有的Ds和Rs,并且交换了所有的Ls和Us,我们得到另一个有效的解决方案,因为这&#34;翻转&#34;解决方案将解决所有已被“翻转”的迷宫。在前方对角线周围 - 这只是所有迷宫的集合。结果是我们只需考虑第一步的解决方案,例如R。
  • 进行A *搜索(或分支定界或完全枚举)的一种方法是在搜索树中的每个节点处记录所有~4000个有效迷宫中的机器人状态。但是我们可以通过结合迄今为止我们的动作无法区分的所有迷宫的状态来节省相当多的时间和空间。我们可以通过记录第三个&#34;未知&#34;单元状态,*。每当我们尝试移动到*单元格时,此状态会分为两个子状态:它成为空单元格的状态(并且我们的移动成功完成),以及它成为一个状态墙(我们保持相同的位置)。如果将此单元格显示为墙壁意味着它不再可能到达目标单元格,则不会生成该子状态。

例如,在尝试第一次移动(R)之后,搜索树节点中的完整状态信息由以下两个部分迷宫组成:

x # * *    . x * *
* * * *    * * * *
* * * *    * * * *
* * * g    * * * g

如果我们尝试D动作,我们最终会:

. # * *    . x * *    . . * *
x * * *    * # * *    * x * *
* * * *    * * * *    * * * *
* * * g    * * * g    * * * g

请注意,从左侧状态的移动必须成功,否则机器人将被装入(1,1)单元格。

另一个例子,以下部分迷宫代表32个不同的完整迷宫(对应于*细胞可以解析的32种不同方式),每个迷宫都有相同的最佳解决方案:

x # * *
. # * *
. # # *
. . . g

虽然仍然可以使用templatetypedef的A * BFS启发式,但每个单元现在可以处于3种状态之一的事实将预计算距离的总数增加到16 * 3 ^ 14 = 76527504,仍然可以管理。我们需要表示可以假设3个状态的项目集合作为3的幂的总和以将索引形成到查找表中,并且这与使用2状态项目一样快速或方便,但是它不是不是硬:唯一昂贵的操作是除以3,可以通过乘以0x55555556并保持64位结果的前32位来完成。

答案 4 :(得分:1)

我很快就把一个实现放在了一起(如果你感兴趣,请参阅here,但这有点乱)。我不确定这是否与@templatetypedef描述的方法类似。我基本上做了以下事情:

  1. 生成所有迷宫并计算从每个单元格到最终单元格的距离(过滤掉所有具有无法到达区域的迷宫,因为这些迷宫相当于在这些点中有墙的迷宫)。
  2. 同时开始穿过所有迷宫。当前得分是指所有尚未到达最终单元格的迷宫上的最终单元格之前的总距离。
  3. 计算四个可能方向中每个方向的分数。贪婪地向最佳(最低)得分的方向移动。
  4. 这种方法趋同,但需要103步。然后我尝试了更大的前瞻,所以不要贪婪地选择最好的下一步,贪婪地选择k下一步的最佳序列。

    我将此方法运行到k = 10,结果如下:

     k | length | sequence
    --------------------
     1 |   103  | RDRDRDRDRLDDRRDRURRDLLDDRURURRDDLLLDDRRDRURRUURRRDDDULLLDDDRRRDLLDDRRLURRLLUDRRDDRRRLUUURRRDDDULLDDRDRR
     2 |    86  | RDRDRDRDRLDDRRDRURRDLLDDRUURRDRDLLLDDRDRURRUURRDRDDULLLDDDRRDRRLULLDDRDRURRLUURURRDDRD
     3 |    79  | RDRDRDRDRLDDRRDRURURDRDLLDDLDRDRRDRURURURRDDDULLLDDRDRRRLULLDDRDRRLURURDRURRDDD
     4 |    70  | RDRDRDRDRLDDRRDRUURRDLLDLDDRDRURUURRDRDDLULLLDDRDRRRDLLDDRRRLUURURRDDD
     5 |    73  | RDRDRDRDRLLDDRRDRURRURRDDDULLLDDRDRDRUURURRDDRDLULLDDRDRRLUURURRDLLLDDRRR
     6 |    70  | RDRDRDRLDRDRDUURRDLLLDDRDRDRURUURRDDULLLDDRDRRRLUUURRRDRLLDDULLDDRDRRR
     7 |    64  | RDRDRDRLDDRRDRLLDDRURUURDRDDULLLDDRDRRDRLURURURRDLDULLDDLLDRDRRR
     8 |    67  | RDRDRDRDLLDDRRDRURUURRDDULLLDDRDDRUURRDRURRDLLLDULLDDRDRRLUURURRDDD
     9 |    64  | RDRDRDRDRLLDDRURRDDRUUURRDDULLLDDRDRDRRLUUURRRDLDULLDDLLDRDRURRD
    10 |    58  | RDRDRDRDRLLLDDRURDRDRUUURRDDDLULLLDRDDRRURRDRRLDDUURURRDDD
    

    当然,对于大k,这种方法变得不可行。由于OP声明问题只能通过29次移动来解决,这种贪婪的方法似乎不是最好的方法......

答案 5 :(得分:1)

让我们考虑一下这一点。

考虑重复模式DRDRDRDRDRDR。我们可以通过呈现类似的东西来绊倒机器人

xx#x
x#xx
xxxx
xx#x

既不以RightRDRDRDRDRDRD)开头也不以DownDRDRDRDRDRDR开头)起作用。


但是,让我们考虑重复模式RRDDRRDDRRDD。为了绊倒机器人,我们需要一个死胡同。让我们考虑一下可能性,看看我们是否能找到一种模式,使两种起始移动都失败(即RRDD)。

1

x#xx
#xxx
xxxx
xxxx 

显然不是一个可以解决的迷宫。

2

xx#x
x#xx
xxxx 
xxxx 

这次旅行RRDDRRDDRRDD。现在,我们可以添加哪些块以使其失败DDRRDDRRDDRR?尝试一下,看看没有办法添加也会阻挡DDRRDDRRDDRR并保持可解决迷宫的块。

3

xxx#
xx#x
xxxx
xxxx

与2相同。

4 5 6 8 9 10

xxxx    xxxx    xxxx    xxxx    xxxx    xxxx
xxx#    x#xx    xx#x    xxxx    xxxx    xxxx
xxxx    #xxx    x#xx    xxx#    x#xx    xx#x
xxxx    xxxx    xxxx    xxxx    #xxx    x#xx

不要旅行。

7

xxxx
xxx#
xx#x
xxxx

显然无法添加块DDRRDDRRDDRRDDRR也会失败并且仍然可以解决。

11 12

xxxx    xxxx
xxxx    xxxx
xxx#    xxxx
xx#x    xxx#

不可解决的迷宫。


考虑到似乎没有迷宫可以同时失败RRDDRRDDRRDDRRDDDDRRDDRRDDRRDDRR,也许可以通过尝试一种模式,向后遍历步骤并从另一种可能性开始来形成解决方案(即,如果我们从RR开始,那么DD,反之亦然。

我希望我有更多的时间来考虑这种需要,在这种情况下,保证越过后面的步骤可以回到起点。

更新

正如评论所指出的那样,两步序列确实失败了很多迷宫。但是,依赖于这个想法的28 DDRRDDRRDDRRLLUURRDDRRDDRRDD的序列,3828个迷宫中的seems to solve 3456。

答案 6 :(得分:1)

我从原始帖子中取出了41根长弦并尝试将其最小化。 我发现可以删除4个字符。不多,但我认为值得注意。 所以从RRDRRRDRLDRDRLDLUULURUUULULLDDRDDDRDURDRD我得到了这个RRDRRDRLDRDRLDLUULRULULLDDRDDDRDURDRD。它通过@ Vincent的方法生成的每个迷宫。

我还检查了这个帖子的其他结果,但没有任何显着差异。

我使用了一些@Vincent的代码来生成迷宫。

这是代码和示例的链接。 http://ideone.com/9OFr5E

如果我在某个地方犯了错误,请告诉我。

答案 7 :(得分:1)

这里的Python代码在给定了最初9个指示的情况下,在95分钟内找到了第三个序列RRDRRDRDLDLDULLLDDRDDURDRRDRR(请注意,在给定了最初9个指示的情况下,Christian Sievers在45分钟内找到了second sequence)。这是进行一些预先计算的深度优先搜索,可能还很幸运。汉斯·奥尔森showed提供了15个初始步骤,我们可以找到更多29个长度的序列(下面的代码在17秒内找到了一个)。

所允许的UL的数量受到限制,这依赖于Christian Sievers的answer中序列的知识以及模式{{1 }}和RLR(以及类似的镜像和旋转对象)作为j_random_hacker noted。另外,还要检查至少一个迷宫中的动作是否确实改变了某些东西(从2432起...简化检查可以节省更多时间),以及检查是否需要预先计算出仍然需要最少移动的方法小于或等于序列中分配的移动(限制为29)。任何一种迷宫状态的编码都是32位。

还请注意,只有2423个可到达的迷宫,而不是本页其他位置出现的3828个。 (后一个列表包括仅在无法到达的地方才不同的迷宫。)

文件RRLL(可用here)包含所有3828,并且代码对其进行了缩小。

mazes.txt

答案 8 :(得分:1)

改进现有解决方案的想法不仅可以基于起始字符串进行搜索,而且可以具有固定的起始字符串和结束字符串。

这足够快,即使没有并行化,您也可以在几分钟内从32步解决方案升级到29步解决方案。

对于m个移动的结束字符串,您会找到正方形,以便从那里开始并应用结束部分序列将到达终点。 (在每个步骤中,您将考虑每个点的两个可能的前任点,并添加端点。正方形S的两个可能的前任者,对于R移动,是正方形S和它的L邻居(如果有效,则为Move) (S,L)。只有在Move(S',R)== S。时才添加其中的每一个。(以后可以发布整个代码。)

然后给他们距离m,邻居的距离m + 1,等等。

然后,您使用此启发式方法进行A *搜索。

从那里走了1分钟 RRDRRDRLDRDLDLDLULLLDDRDDRDURRDRRD(32个长)保持前7个和后11个,给RRDRRDRDLDDULDLDLDRDRDDRDURRDRRD(30个长)并保持前15个然后花了半分钟才能找到 RRDRRDRDLDDULDLDLDRDRDURDRRDR(长29!)

请注意,有2423个可到达的迷宫,但仅需考虑2083个,因为其他迷宫只能通过终点才能到达。

答案 9 :(得分:-1)

不完全是答案,但其他人可能会觉得提出答案很有用。

似乎总体上最好的方法是对角移动。但是,我遇到了一些棘手的情况,我在下面列出了这些情况,这些情况似乎绊倒了我手工提出的方法。

1 0 0 0    1 0 0 0    1 0 0 0    1 0 # Z    1 0 # Z
0 # X #    0 # # X    0 # # X    # 0 X 0    # 0 # Z
0 Z # Z    0 Z Z #    0 0 0 #    Z Z # 0    Z 0 X 0
0 0 0 1    0 0 0 1    X # 0 1    Z Z # 1    Z Z # 1

其中:1是开始/结束。 0是一个空白点,#是一堵墙,Z可能是墙或空的X是麻烦的地方,要么遇到困难,要么遇到困难

一种可以用最少的命令来解决上述迷宫的方法应该非常接近解决任何迷宫。