你能击败英国情报局吗?(非图求解器)


20

现在是时候进行一场艰险的任务,击败英国情报局。这项挑战的目的是编写最短的代码来解决Nonogram。

什么是非图?

非图拼图

规则很简单。您有一个正方形网格,必须用黑色填充或保留为空白。在网格的每一行旁边,列出了该行上黑色正方形的游程长度。在每列上方列出了该列中黑色正方形的游程长度。您的目标是找到所有黑色方块。在这种拼图类型中,数字是离散层析成像的一种形式,可测量给定行或列中有多少个实心正方形的连续线。例如,线索“ 4 8 3”表示按此顺序有四个,八个和三个填充正方形的集合,在连续的组之间至少有一个空白正方形。[ 1 ] [ 2 ]

因此,上述Nonogram的解决方案是:

解决的无图

实施细节

您可以根据需要选择表示Nonogram,并以您认为适合其语言的任何方式将其作为输入。输出也一样。挑战的目的是从字面上完成工作。如果您可以使用程序提供的任何输出来解决非图问题,那是有效的。一个警告是您不能使用在线求解器:)

这个问题在算法上非常具有挑战性(np-complete),因为它没有完全有效的解决方案,因此,您将不会因无法解决较大的问题而受到惩罚,尽管如果您的回答是正确的,则将得到极大的回报。能够处理大案件(请参阅奖金)。作为基准,我的解决方案在5到10秒内可达到大约25x25的分辨率。为了在不同语言之间提供灵活性,对于25x25的非图形,少于5分钟的解决方案就足够了。

您可能总是以正方形NxN非图形式感到困惑。

您可以使用在线非图拼图制造商来测试您的解决方案。

计分

当然,您可以自由使用任何您想要的语言,并且由于这是代码高尔夫球,因此条目将按以下顺序排序:accuracy -> length of code -> speed.但是,请不要因代码高尔夫球语言而气,,所有显示高尔夫球手尝试的语言的答案以一种有趣的方式将被投票!

奖金

其实,我从英国情报部门发布的加密圣诞卡了解Nonograms 这里。第一部分基本上是一个25x25的大型Nonogram。如果您的解决方案能够解决此问题,您将获得荣誉:)

为了使您的数据输入生活更轻松,我提供了如何表示此特定难题的数据,供您免费使用。前25行是行线索,其后是'-'分隔线,随后是25行的col线索,然后是'#'分隔线,然后是填充有正方形线索的网格表示。

7 3 1 1 7
1 1 2 2 1 1
1 3 1 3 1 1 3 1
1 3 1 1 6 1 3 1
1 3 1 5 2 1 3 1
1 1 2 1 1
7 1 1 1 1 1 7
3 3
1 2 3 1 1 3 1 1 2
1 1 3 2 1 1
4 1 4 2 1 2
1 1 1 1 1 4 1 3
2 1 1 1 2 5
3 2 2 6 3 1
1 9 1 1 2 1
2 1 2 2 3 1
3 1 1 1 1 5 1
1 2 2 5
7 1 2 1 1 1 3
1 1 2 1 2 2 1
1 3 1 4 5 1
1 3 1 3 10 2
1 3 1 1 6 6
1 1 2 1 1 2
7 2 1 2 5
-
7 2 1 1 7
1 1 2 2 1 1
1 3 1 3 1 3 1 3 1
1 3 1 1 5 1 3 1
1 3 1 1 4 1 3 1
1 1 1 2 1 1
7 1 1 1 1 1 7
1 1 3
2 1 2 1 8 2 1
2 2 1 2 1 1 1 2
1 7 3 2 1
1 2 3 1 1 1 1 1
4 1 1 2 6
3 3 1 1 1 3 1
1 2 5 2 2
2 2 1 1 1 1 1 2 1
1 3 3 2 1 8 1
6 2 1
7 1 4 1 1 3
1 1 1 1 4
1 3 1 3 7 1
1 3 1 1 1 2 1 1 4
1 3 1 4 3 3
1 1 2 2 2 6 1
7 1 3 2 1 1
#
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

为了方便起见,这里的版本略有不同。逗号分隔的元组(行,列),其中每个元素都是列表列表。

([[7, 3, 1, 1, 7],
  [1, 1, 2, 2, 1, 1],
  [1, 3, 1, 3, 1, 1, 3, 1],
  [1, 3, 1, 1, 6, 1, 3, 1],
  [1, 3, 1, 5, 2, 1, 3, 1],
  [1, 1, 2, 1, 1],
  [7, 1, 1, 1, 1, 1, 7],
  [3, 3],
  [1, 2, 3, 1, 1, 3, 1, 1, 2],
  [1, 1, 3, 2, 1, 1],
  [4, 1, 4, 2, 1, 2],
  [1, 1, 1, 1, 1, 4, 1, 3],
  [2, 1, 1, 1, 2, 5],
  [3, 2, 2, 6, 3, 1],
  [1, 9, 1, 1, 2, 1],
  [2, 1, 2, 2, 3, 1],
  [3, 1, 1, 1, 1, 5, 1],
  [1, 2, 2, 5],
  [7, 1, 2, 1, 1, 1, 3],
  [1, 1, 2, 1, 2, 2, 1],
  [1, 3, 1, 4, 5, 1],
  [1, 3, 1, 3, 10, 2],
  [1, 3, 1, 1, 6, 6],
  [1, 1, 2, 1, 1, 2],
  [7, 2, 1, 2, 5]],
 [[7, 2, 1, 1, 7],
  [1, 1, 2, 2, 1, 1],
  [1, 3, 1, 3, 1, 3, 1, 3, 1],
  [1, 3, 1, 1, 5, 1, 3, 1],
  [1, 3, 1, 1, 4, 1, 3, 1],
  [1, 1, 1, 2, 1, 1],
  [7, 1, 1, 1, 1, 1, 7],
  [1, 1, 3],
  [2, 1, 2, 1, 8, 2, 1],
  [2, 2, 1, 2, 1, 1, 1, 2],
  [1, 7, 3, 2, 1],
  [1, 2, 3, 1, 1, 1, 1, 1],
  [4, 1, 1, 2, 6],
  [3, 3, 1, 1, 1, 3, 1],
  [1, 2, 5, 2, 2],
  [2, 2, 1, 1, 1, 1, 1, 2, 1],
  [1, 3, 3, 2, 1, 8, 1],
  [6, 2, 1],
  [7, 1, 4, 1, 1, 3],
  [1, 1, 1, 1, 4],
  [1, 3, 1, 3, 7, 1],
  [1, 3, 1, 1, 1, 2, 1, 1, 4],
  [1, 3, 1, 4, 3, 3],
  [1, 1, 2, 2, 2, 6, 1],
  [7, 1, 3, 2, 1, 1]])

不幸的是,我的网站已经关闭,但是它曾经有一个相当快的Nonogram求解器;5-10分钟听起来太长了。
尼尔


1
@dwana您无需担心无法解决的情况。至于随机答案,在25x25的非图上,您有2 ^ 625种可能的配置。在上下文中,这是已知宇宙中原子数量的两倍以上(即,如果您只使用宇宙中的每个原子,那么您仍然没有足够的空间来存储可能性)。就时间而言,如果您花了
十亿分之

1
Ty用于澄清无法解决的案件。(+我有一台神奇的PC,可在〜2.1546362E-186秒内验证答案)
dwana

1
您的CSV没有方形提示。这是一些生成它们的JS:s=[].fill([].fill(0,0,25),0,25);s[3][3]=s[3][4]=s3[3][12]=s3[3][13]=s3[3][21]=s[8][6]=s[8][7]=s[8][10]=s[8][14]=s[8][15]=s[8][18]=s[16][6]=s[16][11]=s[16][16]=s[16][20]=s[21][3]=s[21][4]=s[21][9]=s[21][10]=s[21][15]=s[21][20]=s[21][21]=1;
Titus

Answers:


5

Brachylog70 69字节

[R:C]hlL~l:L:1f=.:3aR,.z:3aC,
tL,?he##ElL,E:2a
.<2,_1<
@b:4f:la
e.h1,

这需要两个列表的列表(首先是行指示符,然后是列指示符)。每个指标本身就是一个列表(适用于类似[3,1]一行的情况)。

该版本大约需要3分钟才能解决5 x 5的挑战示例。

方式更有效的版本,91字节

[R:C]hlL~l:L:1f:Cz:3az:Rz:3a=.:4aR,.z:4aC,
tL,?he##ElL,E:2a
.<2,_1<
:+a#=,?h
@b:5f:la
e.h1,

在线尝试!

这不是完全的蛮力:唯一的不同是,这对单元格的值施加了约束,以使每一行和每一列中的1的数目与输入中作为指示符给出的数目相匹配。然后,唯一的蛮力部分就是找到一个网格,其中那些约束与1的“块”相匹配,以作为指示。

在5 x 5的挑战示例中,这一过程大约需要0.05秒。对于奖励情况,这仍然太慢了,因为我不知道如何表达约束条件下被一个或多个零分隔的1的块。

说明

我将在下面解释93字节的版本。两者之间的唯一区别是谓词3的调用(在70字节版本中不存在)以及谓词的编号(因为少了一个)。

  • 主要谓词:

    [R:C]     Input = [R, C]
    hlL       The length of R is L
    ~l        Create a list of length L
    :L:1f     Each element of that list is a sublist of length L with cells 0 or 1 (Pred 1)
              %%% Part unique to the 93 bytes version
    :Cz       Zip the rows of the list of lists with C
    :3a       The sum of 1s in each row is equal to the sum of the indicators (Pred 3)
    z         Transpose
    :Rz       Zip the columns of the list of lists with R
    :3a       The sum of 1s in each column is equal to the sum of the indicators (Pred 3)
              %%%
    =.        Assign values to the cells of the list of lists which satisfy the constraints
    :4aR,     The blocks of 1s must match the indicators on rows
    .z        Transpose
    :4aC,     The blocks of 1s must match the indicators on columns
    
  • 谓词1:强制行具有特定长度,并且每个单元格为0或1。

    tL,       L is the length given as second element of the input
    ?he       Take an element from the list
    ##ElL,    That element E is itself a list of length L
    E:2a      The elements of E are 0s and 1s (Pred 2)
    
  • 谓词2:将变量约束为0或1

    .<2,      Input = Output < 2
    _1<       Output > -1
    
  • 谓词3:列表中1的总和必须等于指标的总和(例如,如果指标为[3:1],则列表中的总和必须为4)

    :+a       Sum the elements of the list and sum the indicator
    #=,       Both sums must be equal
    ?h        Output is the list
    
  • 谓词4:检查1的块是否与指标匹配

    @b        Split the list in blocks of the same value
    :5f       Find all blocks of 1s (Pred 5)
    :la       The list of lengths of the blocks results in the indicator (given as output)
    
  • 谓词5:对于1s的块为true,否则为false

    e.        Output is an element of the input
      h1,     Its first value is 1
    

感觉像是完成工作的理想工具。期待解释。
Emigna '16

@Fatalize太棒了,我正在等待有人使用Prolog风格的语言来完成此操作。您是否尝试过25x25的保护套?我已经
Gowrath's

@gowrath今天下午我将在计算机上运行它,我们将看看会发生什么。
致命

@Fatalize似乎超时,但是我可能做错了。我也不会完全依靠我的数据输入技能:D
gowrath

@gowrath在TIO上超时,但是我将直接在计算机上的脱机解释器上运行它。
致命

9

Haskell,242 230 201 199 177 163 160 149 131字节

import Data.Lists
m=map
a#b=[x|x<-m(chunk$length b).mapM id$[0,1]<$(a>>b),g x==a,g(transpose x)==b]
g=m$list[0]id.m sum.wordsBy(<1)

最后不到200个字节,记入@Bergi。非常感谢@nimi帮助将尺寸减半。

哇。现在几乎是一半大小,部分是因为我,但主要是因为@nimi。

魔术功能是(#)。它查找给定非图的所有解。

这能够解决所有情况,但可能非常慢,因为它的复杂性约为O(2^(len a * len b))。快速基准测试显示86GB分配给5x5的非图形存储空间。

有趣的事实:它适用于所有非图,不仅适用于正方形图。


怎么运行的:

  • a#b:给定表示平方数的整数列表,生成所有网格(map(chunk$length b).mapM id$a>>b>>[[0,1]])并过滤结果以仅保留有效的网格。
  • g:给定一个潜在的非图,它将水平的1的行程相加。

您的意思是O(2 ^(len a * len b)),而不是O((len a * len b)^ 2)。
安德斯·卡塞格

@AndersKaseorg对。将我不小心暗示的百万元保留在此处。:D
ThreeFx

1
另几个字节:m(chunk$l b)replicate(l$a>>b)
BERGI

@ThreeFx 86GB:O ...顺便说一句,您能简要解释一下如何编译它吗?我才刚刚开始学习haskell,这给ghc提供了错误。想测试一下:)
gowrath '16

1
import Data.Lists足够了,因为它可以重新导出Data.ListData.List.Split
nimi 2016年

4

Pyth,91 72 71字节

D:GHdRq@@QdG.nCf.)TrH8V^,01^hQ2=TcNhQ=Y1VhQ=*Y*:H@TH1:H@CTH2)IYjbmjkdTb

一个程序,该程序接受以下形式的列表的输入,[size, [horizontal clues], [vertical clues]]其中每个线索是一个整数列表(空线索是一个空列表,[]),并以二进制网格的形式打印每个解决方案(以换行符分隔),并用1阴影和0无阴影表示。

这是一种蛮力,大约是蛮力O(2^n^2)。对于较大的拼图游戏,开始时会花费很长时间,但是如果有足够的时间,它将可以解决任意大小的拼图。

在线尝试

怎么运行的

该程序通过采用[0, 1]长度等于的重复笛卡尔积来生成所有可能的布局size^2。然后将其分成多个块,为每个水平线提供一个列表。每行都是游程长度编码的,通过的存在进行过滤1并展平,从而留下该行的线索。然后根据输入检查。对块的转置重复上述过程,检查垂直线。如果命中,则将每个块连接起来,然后将连接的块连接到换行符上并隐含地打印出来,并带有尾随的换行符。

D:GHdRq@@QdG.nCf.)TrH8V^,01^hQ2=TcNhQ=Y1VhQ=*Y*:H@TH1:H@CTH2)IYjbmjkdTb  Program. Input: Q
                            hQ                                           Q[0], size
                           ^  2                                          Square
                        ,01                                              [0, 1]
                       ^                                                 Cartesian product
                      V                                     )            For N in the Cartesian product:
                                 cNhQ                                    Split N into Q[0] chunks
                               =T                                        Assign that to T
                                     =Y1                                 Y=1
                                        VhQ                              For H in range [0, Q[0]-1]:
D:GHd                                                                     def :(G, H, d)
                   rH8                                                     Run-length-encode(H)
               f.)T                                                        Filter by presence of 1 in character part
            .nC                                                            Transpose and flatten, giving the clue
       @@QdG                                                               Q[d][G], the relevant input clue
     Rq                                                                    Return clue==input clue
                                               :H@TH1                     :(H, T, 1)
                                                     :H@CTH2              :(H, transpose(T), 2)
                                           =*Y*                           Y=Y*product of above two
                                                             IY           If Y:
                                                                 mjkdT     Conacatenate each element of T
                                                               jb          Join on newlines
                                                                      b    Add a newline and implicitly print

感谢@ Pietu1998提供的一些提示


这可能是我见过的最长的Pyth程序
Business Cat

=ZhZ等于=hZFN等于V
PurkkaKoodari '16

@TheBikingViking给定足够的时间到底是什么意思?我很确定,如果您是从宇宙的概念开始的话,那么到现在为止,它不能解决25x25的问题。
gowrath '16

1
@gowrath我也很确定!我是Pyth的新手,经过这么长时间,我什至不想考虑尝试实现更好的算法
TheBikingViking

2

使用Javascript(ES6),401个 386 333字节

这是早期尝试。效率不是很高,但是我很好奇,要在行和列的二进制表示形式上使用正则表达式测试解决方案。

例如,它将线索[3,1]转换为以下正则表达式:

/^0*1{3}0+1{1}0*$/

目前,此版本尚未考虑到任何线索。我可能会在以后添加。

(c,r)=>{W=c.length;w=[];S=0;M=(n,p)=>eval(`/^0*${p.map(v=>`1{${v}}`).join`0+`}0*$/`).exec(n);R=(y,i=0)=>S||(w[y]=r[y][i],y+1<W?R(y+1):c.every((c,y)=>(n=0,w.map((_,x)=>n+=w[W-1-x][y]),M(n,c)))&&(S=w.join`
`),r[y][i+1]&&R(y,i+1));r=r.map(r=>[...Array(1<<W)].map((_,n)=>((1<<30)|n).toString(2).slice(-W)).filter(n=>M(n,r)));return R(0)}

输出量

解决方案以二进制格式显示。如:

00110
01110
11100
11101
00001

测试

这是对示例网格的简单测试。

let f =
(c,r)=>{W=c.length;w=[];S=0;M=(n,p)=>eval(`/^0*${p.map(v=>`1{${v}}`).join`0+`}0*$/`).exec(n);R=(y,i=0)=>S||(w[y]=r[y][i],y+1<W?R(y+1):c.every((c,y)=>(n=0,w.map((_,x)=>n+=w[W-1-x][y]),M(n,c)))&&(S=w.join`
`),r[y][i+1]&&R(y,i+1));r=r.map(r=>[...Array(1<<W)].map((_,n)=>((1<<30)|n).toString(2).slice(-W)).filter(n=>M(n,r)));return R(0)}

console.log(f(
  [[2],[3],[4],[2],[2]],
  [[2],[3],[3],[3,1],[1]]
));


好主意。但是,这使我的浏览器失去了圣诞节的困惑。
泰特斯(Titus)

2

Haskell,109个字节

免责声明:这来自@ThreeFx的答案。我帮助他降低了答案,但他似乎对包括我最近的重大改进失去了兴趣,因此我将其发布为新答案。

import Data.List
n=mapM id
a#b=[x|x<-n$(n$" #"<$a)<$b,g x==a,g(transpose x)==b]
g=map$max[0].map length.words

用法示例:[[2],[3],[3],[3,1],[1]] # [[2],[3],[4],[2],[2]]-> [[" ## "," ### ","### ","### #"," #"]]

蛮力。尝试的所有组合#,分裂INT块#,计算长度,并比较输入。


1

PHP,751 833(720) 753 724 726 710 691 680 682字节

我渴望构建专门的序列增量,然后再次尝试使用我的笛卡尔生成器;
但是放弃了直角坐标,而倾向于回溯以更快地解决大型难题。

$p=[];foreach($r as$y=>$h){for($d=[2-($n=count($h)+1)+$u=-array_sum($h)+$w=count($r)]+array_fill($i=0,$n,1),$d[$n-1]=0;$i<1;$d[0]+=$u-array_sum($d)){$o=$x=0;foreach($d as$i=>$v)for($x+=$v,$k=$h[$i];$k--;)$o+=1<<$x++;if(($s[$y]|$o)==$o){$p[$y][]=$o;$q[$y]++;}for($i=0;$i<$n-1&$d[$i]==($i?1:0);$i++);if(++$i<$n)for($d[$i]++;$i--;)$d[$i]=1;}}
function s($i,$m){global$c,$w,$p;for(;!$k&&$i[$m]--;$k=$k&$m<$w-1?s($i,$m+1):$k){for($k=1,$x=$w;$k&&$x--;){$h=$c[$x];for($v=$n=$z=$y=0;$k&&$y<=$m;$y++)$n=$n*($f=($p[$y][$i[$y]]>>$x&1)==$v)+$k=$f?:($v=!$v)||$n==$h[$z++];if($k&$v)$k=$n<=$h[$z];}}return$k?is_array($k)?$k:$i:0;}
foreach(s($q,0)as$y=>$o)echo strrev(sprintf("\n%0{$w}b",$p[$y][$o]));
  • 期望数组$r中的提示用于行提示,$c列提示和$s正方形提示。
  • invalid argument supplied for foreach如果找不到解决方案,则抛出该异常。
  • 为了获得正确的字节数,请使用物理命令\n并删除其他两个换行符。

描述

1)从行提示
生成可能满足方形提示的行,
并记住它们对每个行索引的计数。

2)在行组合上回溯:
如果组合满足列提示,则寻求更深的内容或返回成功的组合,
否则尝试该行的下一个可能性

3)打印解决方案


最后一次打高尔夫球对比赛成绩产生了严重影响。
但我删除了最终基准测试的性能分析任务。

替换$n=$n*($f=($p[$y][$i[$y]]>>$x&1)==$v)+$k=$f?:($v=!$v)||$n==$h[$z++];
if(($p[$y][$i[$y]]>>$x&1)-$v){$k=($v=!$v)||$n==$h[$z++];$n=1;}else$n++;
撤消上次高尔夫一步。

例子

对于小示例(17至21大约12 8 7 6.7 5.3毫秒),请使用

$r=[[2],[3],[3],[3,1],[1]];$c=[[2],[3],[4],[2],[2]];$s=[0,0,0,0,0];

圣诞拼图:

  • 用旧的解决方案杀死了我的小型家用服务器
  • 用测试输出杀死浏览器
  • 现在在36秒内以50 37.8 45.5解决

将问题数据放入文件中,christmas.nonogram并使用以下代码导入:

$t=r;foreach(file('christmas.nonogram')as$h)if('-'==$h=trim($h))$t=c;elseif('#'==$h){$t=s;$f=count($h).b;}else
{$v=explode(' ',$h);if(s==$t)for($h=$v,$v=0,$b=1;count($h);$b*=2)$v+=$b*array_shift($h);${$t}[]=$v;}

分解

$p=[];  // must init $p to array or `$p[$y][]=$o;` will fail
foreach($r as$y=>$h)
{
    // walk $d through all combinations of $n=`hint count+1` numbers that sum up to $u=`width-hint sum`
    // (possible `0` hints for $h) - first and last number can be 0, all others are >0
    for(
        $d=[2-
            ($n=count($h)+1)+               // count(0 hint)=count(1 hint)+1
            $u=-array_sum($h)+$w=count($r)  // sum(0 hint) = width-sum(1 hint)
        ]                           // index 0 to max value $u-$n+2
        +array_fill($i=0,$n,1)      // other indexes to 1
        ,$d[$n-1]=0;                // last index to 0
                                    // --> first combination (little endian)
        $i<1;   // $i:0 before loop; -1 after increment; >=$n after the last combination
        $d[0]+=$u-array_sum($d) // (see below)
    )
    {
        // A: create row (binary value) from 1-hints $h and 0-hints $d
        $o=$x=0;
        foreach($d as$i=>$v)
            for($x+=$v,$k=$h[$i];$k--;)
                $o+=1<<$x++;
        // B: if $o satisfies the square hints
        if(($s[$y]|$o)==$o)
        {
            $p[$y][]=$o;    // add to possible combinations
            $q[$y]++;       // increase possibility counter
        }
        // C: increase $d
            // find lowest index with a value>min
                // this loop doesn´t need to go to the last index:
                // if all previous values are min, there is nothing left to increase
        for($i=0;$i<$n-1&$d[$i]==($i?1:0);$i++);
        if(++$i<$n)             // index one up; increase $d if possible
            for($d[$i]++        // increase this value
            ;$i--;)$d[$i]=1;    // reset everything below to 1
            // adjust $d[0] to have the correct sum (loop post condition)
    }
}

// search solution: with backtracking on the row combinations ...
function s($i,$m)
{
    global $c,$w,$p;
    for(;
        !$k // solution not yet found
        &&$i[$m]    // if $i[$m]==0, the previous iteration was the last one on this row: no solution
            --;     // decrease possibility index for row $m
        $k=$k&$m<$w-1? s($i,$m+1) : $k      // if ok, seek deeper while last row not reached ($m<$w-1)
    )
    {
        // test if the field so far satisfies the column hints: loop $x through columns
        for($k=1,$x=$w;$k&&$x--;)   // ok while $k is true
        {
            $h=$c[$x];
            // test column hints on the current combination: loop $y through rows up to $m
            for($v=$n=$z=   // $v=temporary value, $n=temporary hint, $z=hint index
                $y=0;$k&&$y<=$m;$y++)
                // if value has not changed, increase $n. if not, reset $n to 1
                // (or 0 for $k=false; in that case $n is irrelevant)
                $n=$n*  
                    // $f=false (int 0) when value has changed, true (1) if not
                    ($f=($p[$y][$i[$y]]>>$x&1)==$v)
                    +$k=$f?:    // ok if value has NOT changed, else
                        ($v=!$v)        // invert value. ok if value was 0
                        || $n==$h[$z    // value was 1: ok if temp hint equals current sub-hint
                        ++]             // next sub-hint
                ;
            // if there is a possibly incomplete hint ($v==1)
            // the incomplete hint ($n) must be <= the next sub-hint ($c[x][$z])
            // if $n was <$h[$z] in the last row, the previous column hints would not have matched
            if($k&$v)$k=$n<=$h[$z];
        }
        // ok: seek deeper (loop post condition)
        // not ok: try next possibility (loop pre condition)
    }
    return$k?is_array($k)?$k:$i:0;  // return solution if solved, 0 if not
}

// print solution
foreach(s($q,0)as$y=>$o)echo strrev(sprintf("\n%0{$w}b",$p[$y][$o]));

1
大型示例杀死了我的小型家用服务器(500-内部服务器错误)。15秒后即可完成组合,但笛卡尔乘积具有1.823E + 61个成员。(顺便说一句,第7和22行只有一个解决方案。)必须改进算法。
泰特斯(Titus)

我认为,如果您使用递归回溯,可能会加快速度。不过,辛苦了!
gowrath '16

@gowrath:回溯可以提供一点甚至节省字节...带位算术的整数可以提供大约50%的速度,但可以增加大小(必须找出它到底要花多少钱)...我还在。
泰特斯(Titus)

@gowrath:我追赶我的虫子了;它是递增的(还有其他地方?):$d必须按正确的顺序排列foreach
Titus
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.