解决战块剧院难题


13

游戏BattleBlock Theatre有时包含一个谜题,这是Lights Out的广义版本。您有三个相邻的块,每个块表示一个介于1到4之间的水平,包括小节,例如:

|
||||
||

如果触摸一个块,则该块以及任何相邻的块都会增加其级别(从4换回1)。当所有三个块都显示相同的级别(与哪个级别无关)时,难题就解决了。由于触摸块的顺序无关紧要,因此我们通过触摸每个块的频率来表示一种解决方案。上述输入的最佳解决方案是201

|    --> || --> |||     |||
||||     |      ||      |||
||       ||     ||  --> |||

游戏可以很容易地概括出任意数量的方块,尽管对于某些数字而言,并非所有配置都可以解决。

挑战

给定一系列的块级别,返回需要触摸每个块以解决难题的频率。例如,上面的示例将作为给出142并可能产生201结果。如果没有解决方案,请返回您选择的一致输出,这与所有可能的解决方案(例如-1,空字符串)是有区别的。

您可以编写函数或程序,通过STDIN,命令行参数或函数参数以任何方便的列表或字符串格式进行输入,并通过返回值或通过打印到STDOUT进行类似输出。

您的代码应在一分钟内在一台合理的机器上返回所有测试用例的正确结果。(这不是一个完全严格的限制,因此,如果您的解决方案花费一分十秒,那很好,但是如果花费三分钟,那不是很好。好的算法可以在几秒钟内轻松解决它们。)

这是代码高尔夫球,因此最短的答案(以字节为单位)获胜。

例子

解决方案不是唯一的,因此您可能会得到不同的结果。

Input                          Output

1                              0
11                             00
12                             No solution
142                            201
434                            101
222                            000
4113                           0230
32444                          No solution
23432                          10301
421232                         212301
3442223221221422412334         0330130000130202221111
22231244334432131322442        No solution
111111111111111111111222       000000000000000000000030
111111111111111111111234       100100100100100100100133
412224131444114441432434       113013201011001101012133

据我所知,每个输入有4个解决方案,其中块数为0 mod 3或1 mod 3,有0或16个解决方案为2 mod 3。


您需要输出最佳解决方案吗?
xnor 2014年

@xnor不,你不知道。
Martin Ender 2014年

我们是否必须仅打印一种解决方案,还是可以同时打印所有解决方案?
雅库布2014年

@Jakube请问一个。我应该为所有/最佳解决方案添加一个奖励,但我还没有想到的太早,所以任何(一个)解决方案都是如此。
Martin Ender 2014年

Answers:


10

Python 2,115字节

n=input()
for F in range(4):
 t=[F];b=0;exec"x=(-n[b]-sum(t[-2:]))%4;t+=x,;b+=1;"*len(n)
 if x<1:print t[:-1];break

这是我在与马丁讨论问题时编写的程序的高尔夫版本。

输入是通过STDIN的列表。输出是一个列表,表示如果有解决方案,则找到的最后一个解决方案;如果没有,则为零。例如:

>>>
[1, 4, 2]
[2, 1, 1]
>>>
[1, 2]
0
>>>
map(int,"3442223221221422412334")
[2, 3, 3, 2, 1, 3, 2, 0, 0, 2, 1, 3, 2, 2, 0, 0, 2, 2, 3, 1, 1, 3]

Pyth,32个 29字节

V4J]NVQaJ%_+s>J_2@QN4)I!eJPJB

强制端口。感谢@Jakube节省了3个字节。

输入方法与上述相同,请在线尝试


说明(冗长而充满逻辑!)

首先,有两个基本观察:

  • 观察1:触摸块的顺序无关紧要

  • 观察2:如果您触摸4次方块,就相当于触摸了一次

换句话说,如果有解决方案,那么就有解决方案,其中触摸次数在0到3之间(含0和3)。

由于模4非常好,因此我们也可以对模块进行此操作。对于本说明的其余部分,块级别0等效于块级别4。

现在,我们将其表示a[k]为当前的阻止级别,k并表示x[k]我们k在解决方案中触摸阻止的次数。也设n块总数。正如@Jakube指出的那样,解决方案必须满足:

  a[0]   + x[0] + x[1]
= a[1]   + x[0] + x[1] + x[2]
= a[2]          + x[1] + x[2] + x[3]
= a[3]                 + x[2] + x[3] + x[4]
...
= a[n-1]                                     ...  + x[n-2] + x[n-1] + x[n]
= a[n]                                       ...           + x[n-1] + x[n]
= C

C所有块最终的最终级别在哪里,介于0和3之间(包括0和3)(请记住,我们将级别4视为级别0),并且以上所有等式实际上都是模4的全等式。

现在这是有趣的部分:

  • 观察值3:如果存在解决方案,则对于任何最终块级都存在一个解决方案0 <= C <= 3

三种情况基于模3的块数。对每个块的解释都是相同的-对于任何数量的块,都有一个块子集,如果您触摸它们一次,则会将所有块级别增加正好1。

0 mod 3 (touch every third block starting from the second):
    .X. / .X. / .X.

1 mod 3 (touch every third block starting from the first):
    X. / .X. / .X. / .X

2 mod 3 (touch every third block starting from either the first or second):
    X. / .X. / .X. / .X.
    .X. / .X. / .X. / .X

这就解释了为什么有4个解决方案0 mod 31 mod 3,和通常16解决方案2 mod 3。如果您已经有了解决方案,则按上述方法触摸块将提供另一个解决方案,最终以更高的块级别(环绕)结束。

那么这是什么意思?我们可以选择C我们想要的任何最终块级!让我们选择C = 0,因为这节省了字节数。

现在我们的方程变为:

0 = a[0] + x[0] + x[1]
0 = a[1] + x[0] + x[1] + x[2]
0 = a[2] + x[1] + x[2] + x[3]
0 = a[3] + x[2] + x[3] + x[4]
...
0 = a[n-1] + x[n-2] + x[n-1] + x[n]
0 = a[n] + x[n-1] + x[n]

并重新排列:

x[1] = -a[0] - x[0]
x[2] = -a[1] - x[0] - x[1]
x[3] = -a[2] - x[1] - x[2]
x[4] = -a[3] - x[2] - x[3]
...
x[n] = a[n-1] - x[n-2] - x[n-1]
x[n] = a[n] - x[n-1]

因此,我们可以看到的是,如果有的话x[0],那么我们可以使用除最后一个方程式之外的所有方程式来找出彼此x[k]。最后一个方程是我们必须检查的附加条件。

这给了我们一个算法:

  • 尝试所有值 x[0]
  • 使用上述方程式计算所有其他方程式 x[k]
  • 检查是否满足最后一个条件。如果是这样,请保存解决方案。

那给出了上面的解决方案。

那么,为什么有时我们没有解决方案2 mod 3呢?让我们再次看看这两种模式:

X. / .X. / .X. / .X.
.X. / .X. / .X. / .X

现在考虑在这些位置处的方程,即对于第一个方程:

0 = a[0] + x[0] + x[1]
0 = a[3] + x[2] + x[3] + x[4]
0 = a[6] + x[5] + x[6] + x[7]
0 = a[9] + x[8] + x[9] + x[10]

加起来:

0 = (a[0] + a[3] + a[6] + a[9]) + (x[0] + x[1] + ... + x[9] + x[10])

对于第二个:

0 = a[1] + x[0] + x[1] + x[2]
0 = a[4] + x[3] + x[4] + x[5]
0 = a[7] + x[6] + x[7] + x[8]
0 = a[10] + x[9] + x[10]

再次添加它们:

0 = (a[1] + a[4] + a[7] + a[10]) + (x[0] + x[1] + ... + x[9] + x[10])

因此,如果(a[1] + a[4] + a[7] + a[10])(a[0] + a[3] + a[6] + a[9])不相等,那么我们就没有解决方案。但是,如果它们相等,那么我们得到16个解。确实n = 11如此,但是当然可以推广到任何数字,即2 mod 3-从第二个元素开始取每个第三个元素的总和,与从第一个元素开始的每个第三个元素的总和进行比较。

现在最后,是否可以找出必须要做的事情x[0],而不是尝试所有可能性?毕竟,由于我们将目标级别限制C为0,所以只有一个x[0]0 mod 3or或1 mod 3case(as 4 solutions / 4 final levels = 1 solution for a specific final level)中给出解决方案。

答案是……是的!我们可以这样做0 mod 3

 .X..X
.X..X.

转换为:

0 = a[2] + x[1] + x[2] + x[3]   -> 0 = (a[2] + a[5]) + (x[1] + ... + x[5])
0 = a[5] + x[4] + x[5]          /


0 = a[1] + x[0] + x[1] + x[2]   -> 0 = (a[1] + a[4]) + (x[0] + x[1] + ... + x[5])
0 = a[4] + x[3] + x[4] + x[5]   /

减法得出:

x[1] = (a[2] + a[5]) - (a[1] + a[4])

同样,1 mod 3我们可以执行以下模式:

 .X..X.
X..X..X

这使:

x[0] = (a[2] + a[5]) - (a[0] + a[3] + a[6])

这些当然可以通过以3为增量扩展索引来进行概括。

因为2 mod 3,由于我们有两个子集覆盖每个块,因此我们实际上可以选择任意一个x[0]。实际上,这是正确的x[0], x[1], x[3], x[4], x[6], x[7], ...(基本上,任何索引都与都不相同2 mod 3,因为它们没有被任何一个子集覆盖)。

因此,我们可以选择一种方法,x[0]而不是尝试所有可能性...

...但是,坏消息是这不会节省字节数(124字节):

def f(n):s=[];L=len(n);B=sum(n[~-L%3::3])-sum(n[-~L%3::3]);x=A=0;exec"s+=B%4,;A,B=B,-n[x]-A-B;x+=1;"*L*(L%3<2or B<1);print s

聪明。如果您弹出最后一个元素而不是切片,则可以使用J代替而不是H2个字符来保存1 个字符PJ<J_1V4J]NVQaJ%_+s>J_2@QN4)I!eJPJB
雅库布2014年

@Jakube啊,谢谢。当我阅读pop时,我以为就像Python pop从列表中删除时返回最后一个元素。现在我看到情况并非如此。
Sp3000

4

Pyth,72 76 73 66 39 38个字符

Ph+f!eTmu+G%+&H@G_3-@QH@QhH4UtQd^UT2]Y

编辑4:意识到,计算Q[N]-Q[N+1]+solution[-3]Q[-2]-Q[-1]+solution[-3]是ident。因此,我将解决方案的计算量加了1,并过滤了解决方案,其中最后一项为0。幸运的是,这种情况不需要特殊处理。-27个字符

编辑3:从FryAmTheEggman应用一些高尔夫技巧:-7个字符

编辑2:使用筛选,缩小和映射:-3个字符

编辑1:在第一个版本中,如果没有解决方案,我什么也没打印。我认为这是不允许的,因此+4个字符。

期望一个整数列表作为输入[1,4,2][2,0,1]如果有一个整数列表,则输出有效的解决方案,否则为空列表[]

说明:

Q是5级的列表和Y解决方案的列表。以下等式必须成立:

  Q0 + Y0 + Y1 
= Q1 + Y0 + Y1 + Y2
= Q2      + Y1 + Y2 + Y3
= Q3           + Y2 + Y3 + Y4
= Q4                + Y3 + Y4

因此,如果我们使用任何Y0Y1,我们可以计算出Y2Y3Y4以下列方式。

Y2 = (Q0 - Q1     ) mod 4
Y3 = (Q1 - Q2 + Y0) mod 4
Y4 = (Q2 - Q3 + Y1) mod 4

除了最后一个级别以外的所有级别都相等(因为我们没有使用等式= Q4 + Y3 + Y4。要检查该最后一个级别是否也等于其他级别,我们可以简单地检查是否(Q3 - Q4 + Y2) mod 4 == 0。请注意,左边的部分将是值Y5如果我计算解决方案的第六部分,我可以简单地检查它是否为零。

在我的方法中,我只是迭代所有可能的开始([0,0][3,3]),然后再计算length(input)-1个条目,并过滤所有以零结尾的解决方案。

mu+G%+&H@G_3-@QH@QhH4UtQd^UT2   generates all possible solutions

基本上是以下内容:

G = start value           //one of "^UT2", [0,0], [0,1], ..., [9,9]
                          //up to [3,3] would be enough but cost 1 char more
for H in range(len(Q)-1): //"UtQ"
   G+=[(H and G[-3])+(Q(H)-Q(H+1))%4] //"+G%+&H@G_3-@QH@QhH4"
   //H and G[-3] is 0, when H is empty, else G[-3]

然后我将这些可能的解决方案筛选为有效的解决方案:

f!eT //only use solutions, which end in 0

在此解决方案列表中,我附加了一个空列表,以便其中至少包含一项

 +....]Y

并采取第一个解决方案h,弹出最后一个元素p并打印

 Ph

请注意,如果只有一个块,这也将起作用。在我的方法中,我得到了起始位置[0,0],并且没有扩展它。由于最后一项是0,因此它打印解决方案[0]。

第二个特殊情况(2个块)毕竟不是那么特殊。不确定,为什么我之前把事情复杂化了。


我认为没有解决方案就可以打印任何内容,因为这是唯一不打印内容的情况。可能必须让@MartinBüttner进行确认
Sp3000

?**lQ]0qhQeQ<lQ3h+f!%-+ePQ@T_3eQ4mu+G]%+&H@G_3-@QH@QhH4UttQd^UT2]Y是66个字节。性能有些下降,但对我来说,它仍然运行了不到1秒的最大测试用例。如果您想对某些高尔夫球做出解释,请联系我。此注释中没有足够的空间;)希望您喜欢使用Pyth:D
FryAmTheEggman 2014年

+<list><int>具有与相同的效果+<list>]<int>,因此您可以删除第一个]。另外,非常好的解决方案。
isaacg 2014年

@isaacg是否同样适用~?似乎不是我尝试的时间
Sp3000,2014年

@ Sp3000只需替换~a- ~<list>]<int>等效于a<list><int>~+=a而是.append()
isaacg 2014年

3

红宝石,320 313个字符

m=gets.chop.chars.map{|x|x.to_i-1}
a=m.map{0}
t=->n{m[n]+=1
m[n-1]+=1if n>0
m[n+1]+=1if n<m.size-1
m.map!{|x|x%4}
a[n]=(a[n]+1)%4}
t[0]until m[0]==1
(2...m.size).map{|n|t[n]until m[n-1]==1}
r=0
while m.uniq.size>1&&m[-1]!=1
(0...m.size).each_with_index{|n,i|([1,3,0][i%3]).times{t[n]}}
(r+=1)>5&&exit
end
$><<a*''

绝对可以打更多的高尔夫球。对于无法解决的难题,不输出任何内容。

非高尔夫版本:

#!/usr/bin/ruby

nums = gets.chomp.chars.map {|x| x.to_i-1 }
touches = nums.map {0}

# our goal: make all the numbers 1
# utility function
touch = ->n {
    nums[n] += 1
    nums[n-1] += 1 if n > 0
    nums[n+1] += 1 if n < (nums.length-1)
    nums.map! {|x| x % 4 }
    touches[n] = (touches[n] + 1) % 4
}

# first, start with the very first number
touch[0] until nums[0] == 1

# then, go from index 2 to the end to make the previous index right
(2...nums.length).each {|n|
    touch[n] until nums[n-1] == 1
}

iters = 0
if nums.uniq.length != 1
    # I have no idea why this works
    while nums[-1] != 1
        (0...nums.length).each_with_index {|n, i|
            ([1, 3, 0][i % 3]).times { touch[n] }
        }
        if (iters += 1) > 5
            puts -1
            exit
        end
    end
end

puts touches * ''

好的,这很有趣。这是基本的算法,其中{n}在上方的数字上代表n个“触摸” n,如以下示例之一所示:

we want each number to be a 1
first make the first number a 1
3442223221221422412334
2}
1242223221221422412334
 {3} now keep "touch"ing until the number to the left is a 1
1131223221221422412334
  {2}
1113423221221422412334
   {2}
1111243221221422412334
... (repeat this procedure)
1111111111111111111110

我在这里有些难过。如何将111...1110变成一系列相同的数字?因此,我比较了我的解决方案和正确的解决方案(请注意:“触摸”计数都比应有的数字大一,因为输入为1索引,而输出为0索引):

3033233103233301320210
0330130000130202221111

我注意到每个数字都与正确的数字相距一个mod 4,因此我用+s,-s和=s 标记了它们:

3033233103233301320210 original program output
+-=+-=+-=+-=+-=+-=+-=+ amount to modify by (+1, -1, or 0 (=))
4334534404534602621511 result (the correct answer)

0330130000130202221111 (the original solution, digits equal to result mod 4)

这工作了一段时间,直到我注意到有时最终结果111...11112还是11...1113一样!幸运的是,反复应用毫无意义但有效的神奇公式也可以解决这些问题。

所以你有它。该程序一开始就很有意义,但是随着它的发展,它逐渐变得越来越丑陋。我认为,这是典型的标准高尔夫解决方案。:)


1
我喜欢您的代码中的最后一条评论:)。通过更改exit if (r+=1)>5为,您可以节省2个字符(r+=1)>5&&exit。此外,(code)while cond语法比短while cond \n code \n end
Cristian Lupascu 2014年

2

Python 2,294,289,285,281 273字节

n=input();l=len(n);s=[0]*l
for i in range(2,l):
 a=(n[i-2]-n[i-1])%4;s[i]+=a;n[i-1]+=a;n[i]+=a
 if i+1<l:n[i+1]+=a
 n=[a%4for a in n]
if l%3>1 and n!=[n[0]]*l:print"x"
else:
 for i in range(l%3,l-1,3):s[i]+=(n[l-1]-n[l-2])%4
 m=min(s);s=[(a-m)%4 for a in s];print s

演示

我相信这可以继续打下去。

这是测试用例的结果:

[1]
-> [0]

[1,1]
-> [0, 0]

[1,2]
-> x

[1,4,2]
-> [2, 0, 1]

[4,3,4]
-> [1, 0, 1]

[2,2,2]
-> [0, 0, 0]

[4,1,1,3]
-> [0, 2, 3, 0]

[3,2,4,4,4]
-> x

[2,3,4,3,2]
-> [0, 0, 3, 3, 1]

[4,2,1,2,3,2]
-> [2, 0, 2, 3, 3, 1]

[3,4,4,2,2,2,3,2,2,1,2,2,1,4,2,2,4,1,2,3,3,4]
-> [0, 3, 3, 0, 1, 3, 0, 0, 0, 0, 1, 3, 0, 2, 0, 2, 2, 2, 1, 1, 1, 1]

[2,2,2,3,1,2,4,4,3,3,4,4,3,2,1,3,1,3,2,2,4,4,2]
-> x

[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2]
-> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0]

[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,3,4]
-> [1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 3, 3]

[4,1,2,2,2,4,1,3,1,4,4,4,1,1,4,4,4,1,4,3,2,4,3,4]
-> [1, 0, 3, 0, 0, 3, 2, 3, 1, 0, 0, 1, 0, 3, 1, 1, 3, 1, 0, 0, 2, 1, 2, 3]

该算法首先确保除最后一个块外的所有块的值都相同(通过迭代并添加除前两个块外的所有块的“触摸计数”)。然后,如果允许的块数为((num_of_blocks - 1) % 3 != 1),请返回并确保其余块的值与最后一个块匹配。x如果没有解决方法,则打印。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.