我遇到了这个非常有趣的问题,我们有一个4x4的迷宫和一个机器人试图进入目标。问题是,您必须找到一系列预定义命令,这些命令将始终导致机器人达到目标。
我们假设我们有一个像这样的迷宫:
x . . .
. # # .
. # # .
. . . g
这个特殊的迷宫可以用例如命令序列DDDRRR
或RRRDDD
来解决,其中R =右,L =左,U =上,D =下(duh)。 / p>
然而,这些序列都不会解决这个迷宫:
x . # .
. . . .
# . . .
. . . g
机器人始终从左上角开始,目标始终位于右下角,迷宫始终是2D 4x4矩阵。
我已经实现了一个算法,它让我获得了78个命令的获胜序列。我确信至少存在29个命令的解决方案(其他人完成了这个)。
这个问题实际上已经存在了几年,所以我丢失了当时使用的算法,但基本的想法是在我生成的所有迷宫中搜索,并始终选择路径结果解决了最多的迷宫。这实际上让我得到了一个略长于78的序列;我手动减少了一些命令,我发现这些命令是多余的。
是的,暴力迫使将需要多年的时间。
如果我的记忆服务,可能会有少于4000个迷宫(可能的迷宫是存在左上角和右下角之间的路径)。
OH!机器人在执行命令期间至少一次访问目标就足够了。也就是说,在最后一个命令之后,它不必坐在目标上。
我是否抓住了任何人的兴趣?我应该如何处理这个问题以获得更有效的答案?感谢您考虑:)
它是(非常)匆忙拼凑出一块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目标,但我仍然在寻找具有相同口径的程序化获取解决方案。从序列中删除字符时我没有使用逻辑 - 我只是删除了一个字符并检查它是否解决了所有迷宫,冲洗并重复。
答案 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
遍及所有迷宫
特定迷宫的可达位置。
可达位置包括值start
和stop
开始位置和目标。
让d
跨越四个可能的方向。
(程序将m
输出为十六进制的14位数字,对墙壁进行编码
位置,p
为大写字母。它使用变量名
不一致:n
代表m
或i
或len
,w
(墙壁)代表m
,
s
(步骤)代表i
,在某些情况下,h
(助手)代表d
。)
对于每个i<len
和每个d
,都有一个变量D<i><d>
指示
解决方案的第i
步是朝方向d
前进。
(程序使用dir
函数创建它们。)
对于每个i0<len
,都有一些子句要求最多
四个变量D<i0><d>
是正确的。
对于每个m
,i
和p
,都有一个变量P<m><i><p>
表示
在迷宫m
中,在时间i
到达位置p
。
(程序使用pos
函数创建它们。)
对于每个迷宫m0
,都有一个单位子句P<m0><0><start>
建立
起始位置。
对于每个m0
和i0
,都有一些子句要求:
变量P<m0><i0><p>
为真(我们不能处于两个不同的位置)。
除了i0=0
(可以替换的情况)之外,这些都是多余的
对于所有~P<m0><0><p>
的单位条款p!=start
),但似乎有帮助。
根据时间从i0
到时间i0+1
的迷宫的进展
D<i0><d>
中给出的方向是使用辅助变量来描述的。
对于每个m
,i>0
,p
和d
,都有变量
P<m><i><p><d>
。
(程序使用posh
函数创建它们。将它们打印为
<d><m><i><p>
,以便将变量名的长度保持在
Knuth的程序规定了8个字符。)
这个想法是每个方向都给出一个位置可能的原因
可能达到。该变量表示
在迷宫m
中,在时间i
处位置p
是“由于” d
引起的。
如果我们考虑朝某个方向前进,撞墙然后从
它来自那个方向,那么我们可以解释变量
就是从d
方向到达那个位置。
因此,让我们考虑一些固定的m
,i<len
,p
和d
。
P<m><i+1><p>
为何由于d
而成立?
如果没有d
方向的墙壁(来自p
),
那么我们可能是从那里来的;我们将该位置称为np
。
如果有一堵墙,那么我们可能已经
在此之前,试图去那里撞墙。
因此,我们需要建立以下条款
P<m><i+1><p><d>
等价于
P<m><i><p'>
和D<i><d'>
,其中p'=np
和d'
是相反的方向
如果没有墙,则d
的值;如果有墙,则p'=p
和d'=d
的值。
(程序在函数mazestepdirposconds
中执行此操作。)
然后,我们只需要建立用于为每个m0
,i0>0
和p0
的子句,
变量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)
一些想法:
g
的机器人在三个动作的第二个中达到g
时才适用。棘手!] 强> *
。每当我们尝试移动到*
单元格时,此状态会分为两个子状态:它成为空单元格的状态(并且我们的移动成功完成),以及它成为一个状态墙(我们保持相同的位置)。如果将此单元格显示为墙壁意味着它不再可能到达目标单元格,则不会生成该子状态。例如,在尝试第一次移动(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描述的方法类似。我基本上做了以下事情:
这种方法趋同,但需要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
既不以Right
(RDRDRDRDRDRD
)开头也不以Down
(DRDRDRDRDRDR
开头)起作用。
但是,让我们考虑重复模式RRDDRRDDRRDD
。为了绊倒机器人,我们需要一个死胡同。让我们考虑一下可能性,看看我们是否能找到一种模式,使两种起始移动都失败(即RR
或DD
)。
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#
不可解决的迷宫。
考虑到似乎没有迷宫可以同时失败RRDDRRDDRRDDRRDD
和DDRRDDRRDDRRDDRR
,也许可以通过尝试一种模式,向后遍历步骤并从另一种可能性开始来形成解决方案(即,如果我们从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秒内找到了一个)。
所允许的U
和L
的数量受到限制,这依赖于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
是麻烦的地方,要么遇到困难,要么遇到困难
一种可以用最少的命令来解决上述迷宫的方法应该非常接近解决任何迷宫。