非图线暴力破解


25

背景

Nonogram,也称为Picross或Griddlers,是一个难题,其目标是使用每行上连续的彩色单元格的数量来确定2D网格上的每个单元格是彩色还是空白。

以下是带有解决方案的示例非图拼图。

问题是,某些商业Nonogram游戏/移动应用程序具有无法用手解决的难题(例如,具有多种解决方案,或者需要深度回溯)。但是,它们也为玩家提供了一些生命,当您尝试给正确答案为空白的单元格上色时将会失去生命。因此,现在是时候对那些令人讨厌的“难题”进行暴力破解了!

为了简化任务,想象一下只有一条线索及其线索:

3 7 | _ _ _ _ _  _ _ _ _ _  _ _ _ _ _

这些[3,7]是线索,行长为15个单元格。由于它有多种可能的解决方案,因此我们需要冒一些生命危险才能完全解决此问题(即确定所有有色单元格)。

挑战

给定一条具有线索的行(正整数列表)和行长,假设您使用最佳策略对这条行进行暴力破解,找出将要失去的最大生命数。

请注意,您总是会猜到有色单元格。在实际的游戏中,猜测空单元(对还是错)对您的生活没有影响,因此您无法以这种方式“解决”难题。

另外,您可以假设输入始终代表有效的Nonogram行,因此您无需担心诸如的问题[6], 5

说明

首先让我们看一些简单的例子。

[1,2], 5

此行完全有三种可能性(O一个彩色单元格,.一个空单元格):

O . O O .
O . . O O
. O . O O

如果我们尝试为单元格0(从左开始的基于0的索引)着色,则会发生以下情况之一:

  • 单元格已正确着色。现在,我们有两种可能性,我们可以在单元2和单元4之间进行选择,以完全求解直线。无论哪种情况,在最坏的情况下我们都会丧生。
  • 牢房是空的,我们失去了生命。在这种情况下,我们已经确定了这条线的唯一解决方案,因此我们只损失了1条生命。

因此,答案[1,2], 5是1。

[5], 10

二进制搜索?不。

最明显的第一选择是4或5,这将显示一个空白的可能性(以1个生命为代价)。假设我们先选择了4个。如果单元格4确实是彩色的,则将其向左扩展,即尝试3、2、1和0直到失去生命(或者单元格0带有颜色,那么我们最终将完全没有生命)。每当失去生命时,我们都可以唯一地确定解决方案,例如,如果我们看到这样的情况:

_ _ X O O _ _ _ _ _

那么我们已经知道答案是这样的:

. . . O O O O O . .

因此,答案[5], 10也是1。

[3,7], 15

从单元格11开始,如果为空,将立即显示以下解决方案。

O O O . O O O O O O O X . . .

然后尝试12,如果为空,则有两种可能,但要花1个额外的时间才能解决。

O O O . . O O O O O O O X . .
. O O O . O O O O O O O X . .

现在尝试2。如果为空,将导致三种可能性,可以与[1,2], 5示例类似地解决。

. . X O O O . O O O O O O O .
. . X O O O . . O O O O O O O
. . X . O O O . O O O O O O O

如果您以这种方式将风险最小化,则可以以最大 花了2个生命。

测试用例

[1,2] 5 => 1
[2] 5 => 2
[1] 5 => 4
[] 5 => 0
[5] 10 => 1
[2,1,5] 10 => 0
[2,4] 10 => 2
[6] 15 => 2
[5] 15 => 2
[4] 15 => 3
[3,7] 15 => 2
[3,4] 15 => 3
[2,2,4] 15 => 4
[1,1,1,1,1,1,1] 15 => 2

[2,1,1,3,1] 15 => 3
[1,1,1,2,1] 15 => 5

对于后两种情况,最佳策略不是经过最小的空白,而是从左到右(或从右到左)。感谢@crashoz指出这一点。

规则

适用标准规则。有效提交的最短字节数为准。

赏金

如果有人提出多项式时间算法(带有正确性证明),我将奖励+100赏金给这种解决方案。


预期的输出是[6], 5什么?
Leaky Nun

当您进行猜测时,是否必须猜测单元格是黑色的,还是可以猜测是黑色还是白色?
feersum

@LeakyNun这是无效行。您可以假定输入始终是有效的Nonogram行。
Bubbler

@feersum您总是猜到有色细胞。在实际的游戏中,猜测一个空的单元格(对还是错)不会影响您的生活,因此您无法获得任何反馈。
Bubbler

奇妙的挑战
Enrico Borba

Answers:


19

红宝石,85字节

f=->l,n,s=n-l.sum-l.size+1{*a,b=l;b&&s>0?(a[0]?1+f[a,n-b-2,s-1]:(n.to_f/b).ceil-1):0}

在线尝试!

说明

l=[l1,l2,...,lx]xn

lx
nlx
nlx1+f(l,nlx)
1+f(l~,nlx2)l~l

f(l,n)={0,if 1xli+x1nnlx1if x=11+max{f(l,nlx)f(l~,nlx2),otherwise

这是一个_未知的例子,X是一个已知的空间,O是一个已知的有色细胞,L是丧命的

[2,2,4] 15                  _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
(1) -> [2,2,4] 11           _ _ _ _ _ _ _ _ _ _ _ L X X X
    (1) -> [2,2,4] 7        _ _ _ _ _ _ _ L X X X L X X X
        0                   X X X L X X X L X X X L X X X
    (2) -> [2,2] 5          _ _ _ _ _ X O O O O L L X X X
        0                   O O X O O X O O O O L L X X X 
(2) -> [2,2] 9              _ _ _ _ _ _ _ _ _ X O O O O L
    (1) -> [2,2] 7          _ _ _ _ _ _ _ L X X O O O O L
        (1) -> [2,2] 5      _ _ _ _ _ L X L X X O O O O L
            0               O O X O O L X L X X O O O O L
        (2) -> [2] 3        _ _ _ X O O L L X X O O O O L
            1               O O L X O O L L X X O O O O L               
    (2) -> [2] 5            _ _ _ _ _ X O O L X O O O O L
        2                   O O L L X X O O L X O O O O L

它生成一棵二叉树,要获取失去的生命数量,我们只需要计算树的最大高度即可。但这使它成为因为我们评估了所有分支。我们可以做得更好。O(2n)

让我们定义一个成本函数,它将帮助我们在每个步骤中“选择”正确的分支。h

h(l,n)=n1xlix+1

h如果我们将所有线索打包在一起,并且它们之间有一个空格,那么就是我们拥有的多余空格的数量。因此,从本质上说,这是我们解决实例需要多少生命的指标,它越高,需要的生命就越多。想法是将此指标应用于我们的递归公式。

根据定义, h

h(l,nlx)=nlx1xlix+1=(n1xlix+1)lx=h(l,n)lx

并且,

h(l~,nlx2)=nlx21x1li(x1)+1=(n1xlix+1)1=h(l,n)1

然后,

h(l,n)={0,if 1xli+x1nnlx,if x=1max{h(l,nlx)+lxh(l~,nlx2)+1,otherwise

我们希望在每个步骤中最大化 以得到最坏的情况,因此让我们检查循环中两个表达式之间的差异h

[h(l,nlx)+lx][h(l~,nlx2)+1]=nlxn1xlix+1+lx[nlx21x1li(x1)+1+1]=2

[h(l,nlx)+lx][h(l~,nlx2)+1]=2[h(l,nlx)+lx][h(l~,nlx2)+1]<0[h(l,nlx)+lx]<[h(l~,nlx2)+1]
因此第二个表达式始终是最大值,我们可以删除第一个表达式

h(l,n)={0,if 1xli+x1nnlx,if x=1h(l~,nlx2)+1otherwise

最后,递归定义向我们表明,函数中的选项(2)始终是最坏的情况(给出最大可能性,即最大化)hfh

f(l,n)={0,if 1xli+x1nnlx1if x=11+f(l~,nlx2),otherwise

我们每减少至少三步,现在就有一个递归调用,复杂度为nO(n)


2
欢迎来到PPCG,令人难以置信的第一篇文章!
科尔

1
@cole这不是他们的第一篇文章,但它的确令人难以置信!非常聪明的方法+1
Xcoder先生,18年

1
很棒的工作。如果在此之前没有人发现任何严重的逻辑缺陷,我将在两天后颁发赏金。
Bubbler

2

Python中,303个 289字节

长时间打高尔夫球,所以可能会有很多多余的脂肪。(感谢Jo King找到了14个字节的值。)

函数f会生成所有可能的排列方式(尽管始终以空格作为第一个字符,但是只要在调用它之前将长度加1,就可以了)。函数g选择空白和递归最少的位置。函数h将它们放在一起。

f=lambda l,n:["."*i+"X"*l[0]+c for i in range(1,n-l[0]+1)for c in f(l[1:],n-i-l[0])]if l else["."*n]
def g(q,n):O,X=min([[[p[:i]+p[i+1:]for p in q if p[i]==u]for u in".X"]for i in range(n)],key=lambda x:len(x[0]));return(len(q)>1)*1and max(1+g(O,n-1),g(X,n-1))
h=lambda l,n:g(f(l,n+1),n+1)

这些示例都运行良好:

>>> h([3,7],15)
2
>>> h([3,4],15)
3
>>> h([1,1,1,2,1],15)
6


1
你被允许返回False0?如果是这样,您可以更改(len(q)>1)*1andlen(q)>1and。如果你不能恢复False0,那么做到这一点,但变化g(f(l,n+1),n+1)1*g(f(l,n+1),n+1),它仍然会保存一个字节
扎卡里

1
更妙的是:在情况下False是不允许的0,而不是改变g(f(l,n+1),n+1)1*g(f(l,n+1),n+1),将其更改为+g(f(l,n+1),n+1)
扎卡里

2
此外,您不必算h=在你的字节数
扎卡里

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.