构建非地理幅度优化器™


12

非图是日本的益智游戏,其目标是根据连续区域的列表绘制黑白图片,如下所示:

带“ lambda”的示例非图。

将行或列的非地理大小定义为该行或列中连续黑色区域的数量。例如,第一行的非地理大小为1,因为该行中有一个2平方的区域。第8行的非地理大小为3,因为它的第2行,第2行,第1行。

空行或列的非地理大小为0。


您的任务是编写一个程序,使用一个非图的解决方案网格,并生成一个具有尽可能小的填充正方形的解决方案网格,其中每一行和每一列与给定的解决方案网格具有相同的非成像性。

例如,填充了所有正方形的非图格网在每行或每列上的非图量级为1:

一个10x10的非方格图,其中每个正方形均已填充。

只需在网格上有一条对角线条纹,就可以大大减少实心方块的数量,从而达到相同的非影像大小:

具有与上述相同的非原版大小的10x10非原图。


您的程序将从该文件中接收到50,000行的输入(1.32 MB tar.gz文本文件; 2.15 MB未压缩),每行代表一个16×16非图解决方案网格,其中随机填充(80%黑色)正方形。输出另外50,000行,每行包含对应输入网格的优化解决方案网格。

每个网格都表示为具有43个字符的base64字符串(从左到右,然后从上到下编码正方形),您的程序将需要以相同格式返回其输出。例如,文件中的第一个网格为E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=,并呈现为:

第一个示例非图

网格从其开始E映射到000100,因此第一行的前六个单元格除第四个单元格外均为白色。下一个字符是/映射到的字符111111,因此接下来的6个单元格全为黑色-依此类推。


您的程序实际上必须为50,000个测试用例中的每一个返回一个具有正确的非地理大小的解决方案网格。如果没有发现更好的结果,则可以返回与输入相同的网格。

返回最少填充总数(以任何语言显示)的程序是赢家,而较短的代码是决胜局。


当前记分牌:

  1. 3,637,260 —爪哇Sleafar
  2. 7,270,894 -Matlab瑕疵
  3. 10,239,288 —乔·Z·巴什(Bash)

1
我真的看不到base 64编码的要点,使用它确实不是很痛苦。只用1和0组成一行会更容易吗?还是将整个内容编码为位图?
瑕疵的

@flawr:主要减少文件大小(与1和0相比减少了6倍)。而且,位图将更加难以使用。
Joe Z.

您可以制作一张黑白图像,易于读取/写入,并且大小与b64编码相同。
瑕疵的

2
也不是b64编码的迷,用于输入和/或输出。为什么不让I / O采用任何方便的格式?
Sparr

1
假设是这样,明天明天这个时候我应该有一个最佳的解决方案。
quintopia '16

Answers:


7

Python 2和PuLP-2,644,688平方(已最小化);10,753,553平方(最佳最大化)

最少打高尔夫球到1152字节

from pulp import*
x=0
f=open("c","r")
g=open("s","w")
for k,m in enumerate(f):
 if k%2:
    b=map(int,m.split())
    p=LpProblem("Nn",LpMinimize)
    q=map(str,range(18))
    ir=q[1:18]
    e=LpVariable.dicts("c",(q,q),0,1,LpInteger)
    rs=LpVariable.dicts("rs",(ir,ir),0,1,LpInteger)
    cs=LpVariable.dicts("cs",(ir,ir),0,1,LpInteger)
    p+=sum(e[r][c] for r in q for c in q),""
    for i in q:p+=e["0"][i]==0,"";p+=e[i]["0"]==0,"";p+=e["17"][i]==0,"";p+=e[i]["17"]==0,""
    for o in range(289):i=o/17+1;j=o%17+1;si=str(i);sj=str(j);l=e[si][str(j-1)];ls=rs[si][sj];p+=e[si][sj]<=l+ls,"";p+=e[si][sj]>=l-ls,"";p+=e[si][sj]>=ls-l,"";p+=e[si][sj]<=2-ls-l,"";l=e[str(i-1)][sj];ls=cs[si][sj];p+=e[si][sj]<=l+ls,"";p+=e[si][sj]>=l-ls,"";p+=e[si][sj]>=ls-l,"";p+=e[si][sj]<=2-ls-l,""
    for r,z in enumerate(a):p+=lpSum([rs[str(r+1)][c] for c in ir])==2*z,""
    for c,z in enumerate(b):p+=lpSum([cs[r][str(c+1)] for r in ir])==2*z,""
    p.solve()
    for r in ir:
     for c in ir:g.write(str(int(e[r][c].value()))+" ")
     g.write('\n')
    g.write('%d:%d\n\n'%(-~k/2,value(p.objective)))
    x+=value(p.objective)
 else:a=map(int,m.split())
print x

(注意:缩进的行以制表符开头,而不是空格。)

输出示例:https : //drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing

事实证明,诸如此类的问题很容易转换为整数线性程序,因此我需要一个基本的问题来学习如何为自己的项目使用PuLP(适用于各种LP解算器的python接口)。事实证明,PuLP极其易于使用,并且没有高尔夫球的LP构建器在我第一次尝试时就可以完美运行。

使用分支定界IP求解器为我完成艰苦的工作(而不是不必实现分支定界求解器)的两个好处是:

  • 专门构建的求解器非常快。该程序可以在我相对低端的家用PC上在大约17个小时内解决所有50000个问题。每个实例需要1-1.5秒才能解决。
  • 他们提供有保证的最佳解决方案(或告诉您他们没有这样做)。因此,我可以确信没有人会在方格中击败我的得分(尽管有人可能会打平它并在高尔夫球部分击败我)。

如何使用这个程序

首先,您需要安装PuLP。pip install pulp如果您已经安装了pip,应该可以解决问题。

然后,您需要将以下内容放入名为“ c”的文件中:https : //drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing

然后,从同一目录在任何最新的Python 2构建中运行该程序。在不到一天的时间内,您将拥有一个名为“ s”的文件,其中包含50,000个已解决的非图网格(以可读格式),每个网格下面都列出了填充的正方形总数。

如果您想最大化实心方块的数量,请改为将LpMinimize第8行的LpMaximize改为。您将非常像这样获得输出:https : //drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharing

输入格式

该程序使用修改后的输入格式,因为Joe Z.说如果我们希望在OP中发表评论,我们将被允许对输入格式进行重新编码。单击上面的链接以查看外观。它由10000行组成,每行包含16个数字。偶数行是给定实例行的大小,而奇数行是同一实例列与其上的行的大小。该文件是由以下程序生成的:

from bitqueue import *

with open("nonograms_b64.txt","r") as f:
    with open("nonogram_clues.txt","w") as g:
        for line in f:
            q = BitQueue(line.decode('base64'))
            nonogram = []
            for i in range(256):
                if not i%16: row = []
                row.append(q.nextBit())
                if not -~i%16: nonogram.append(row)
            s=""
            for row in nonogram:
                blocks=0                         #magnitude counter
                for i in range(16):
                    if row[i]==1 and (i==0 or row[i-1]==0): blocks+=1
                s+=str(blocks)+" "
            print >>g, s
            nonogram = map(list, zip(*nonogram)) #transpose the array to make columns rows
            s=""
            for row in nonogram:
                blocks=0
                for i in range(16):
                    if row[i]==1 and (i==0 or row[i-1]==0): blocks+=1
                s+=str(blocks)+" "
            print >>g, s

(此重新编码程序还为我提供了额外的机会来测试我为上述同一项目创建的自定义BitQueue类。它只是一个队列,可以将数据作为位或字节序列推入该队列,并可以从中将数据一次弹出一个字节或一个字节。在这种情况下,效果很好。)

由于要构建ILP的特定原因,我对输入进行了重新编码,有关用于生成幅度的网格的额外信息完全没有用。幅度是唯一的约束,因此幅度是我需要访问的全部。

空洞的ILP构建器

from pulp import *
total = 0
with open("nonogram_clues.txt","r") as f:
    with open("solutions.txt","w") as g:
        for k,line in enumerate(f):
            if k%2:
                colclues=map(int,line.split())
                prob = LpProblem("Nonogram",LpMinimize)
                seq = map(str,range(18))
                rows = seq
                cols = seq
                irows = seq[1:18]
                icols = seq[1:18]
                cells = LpVariable.dicts("cell",(rows,cols),0,1,LpInteger)
                rowseps = LpVariable.dicts("rowsep",(irows,icols),0,1,LpInteger)
                colseps = LpVariable.dicts("colsep",(irows,icols),0,1,LpInteger)
                prob += sum(cells[r][c] for r in rows for c in cols),""
                for i in rows:
                    prob += cells["0"][i] == 0,""
                    prob += cells[i]["0"] == 0,""
                    prob += cells["17"][i] == 0,""
                    prob += cells[i]["17"] == 0,""
                for i in range(1,18):
                    for j in range(1,18):
                        si = str(i); sj = str(j)
                        l = cells[si][str(j-1)]; ls = rowseps[si][sj]
                        prob += cells[si][sj] <= l + ls,""
                        prob += cells[si][sj] >= l - ls,""
                        prob += cells[si][sj] >= ls - l,""
                        prob += cells[si][sj] <= 2 - ls - l,""
                        l = cells[str(i-1)][sj]; ls = colseps[si][sj]
                        prob += cells[si][sj] <= l + ls,""
                        prob += cells[si][sj] >= l - ls,""
                        prob += cells[si][sj] >= ls - l,""
                        prob += cells[si][sj] <= 2 - ls - l,""
                for r,clue in enumerate(rowclues):
                    prob += lpSum([rowseps[str(r+1)][c] for c in icols]) == 2 * clue,""
                for c,clue in enumerate(colclues):
                    prob += lpSum([colseps[r][str(c+1)] for r in irows]) == 2 * clue,""
                prob.solve()
                print "Status for problem %d: "%(-~k/2),LpStatus[prob.status]
                for r in rows[1:18]:
                    for c in cols[1:18]:
                        g.write(str(int(cells[r][c].value()))+" ")
                    g.write('\n')
                g.write('Filled squares for %d: %d\n\n'%(-~k/2,value(prob.objective)))
                total += value(prob.objective)
            else:
                rowclues=map(int,line.split())
print "Total number of filled squares: %d"%total

这是实际产生上面链接的“示例输出”的程序。因此,每个网格末端的超长弦在打高尔夫球时都会被截断。(打高尔夫球的版本应产生相同的输出,减去单词"Filled squares for "

怎么运行的

cells = LpVariable.dicts("cell",(rows,cols),0,1,LpInteger)
rowseps = LpVariable.dicts("rowsep",(irows,icols),0,1,LpInteger)
colseps = LpVariable.dicts("colsep",(irows,icols),0,1,LpInteger)

我使用18x18网格,中间的16x16部分是实际的拼图解决方案。cells是这个网格。第一行创建324个二进制变量:“ cell_0_0”,“ cell_0_1”,依此类推。我还创建了网格的解决方案部分中的单元格之间和周围的“空间”的网格。rowseps指向289个变量,这些变量表示水平分隔单元格的空间,而colseps类似地指向标记垂直分隔单元格的空间的变量。这是一个unicode图:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|□|0
  - - - - - - - - - - - - - - - - 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

0S和S被跟踪的二进制值cell的变量时,|s为二进制值跟踪由rowsep变量,和-s为二进制值跟踪由colsep变量。

prob += sum(cells[r][c] for r in rows for c in cols),""

这是目标函数。只是所有cell变量的总和。由于这些是二进制变量,因此这恰好是解决方案中的实心方块数。

for i in rows:
    prob += cells["0"][i] == 0,""
    prob += cells[i]["0"] == 0,""
    prob += cells["17"][i] == 0,""
    prob += cells[i]["17"] == 0,""

这只是将围绕网格外部边缘的像元设置为零(这就是为什么我在上方将它们表示为零)的原因。这是跟踪已填充多少个“块”单元格的最便捷方法,因为它可以确保从未填充到填充(跨列或行移动)的每个更改都与从填充到未填充(反之亦然)的相应更改匹配),即使该行的第一个或最后一个单元格都已填充。这是首先使用18x18网格的唯一原因。这不是计数块的唯一方法,但我认为这是最简单的方法。

for i in range(1,18):
    for j in range(1,18):
        si = str(i); sj = str(j)
        l = cells[si][str(j-1)]; ls = rowseps[si][sj]
        prob += cells[si][sj] <= l + ls,""
        prob += cells[si][sj] >= l - ls,""
        prob += cells[si][sj] >= ls - l,""
        prob += cells[si][sj] <= 2 - ls - l,""
        l = cells[str(i-1)][sj]; ls = colseps[si][sj]
        prob += cells[si][sj] <= l + ls,""
        prob += cells[si][sj] >= l - ls,""
        prob += cells[si][sj] >= ls - l,""
        prob += cells[si][sj] <= 2 - ls - l,""

这是ILP逻辑的实质。基本上,它要求每个单元格(第一行和第一列中的单元格除外)是该单元格和分隔符的逻辑异或,直接在其行的左侧并在其列的正上方。我从这个奇妙的答案中得到了在{0,1}整数程序中模拟异或的约束:https : //cs.stackexchange.com/a/12118/44289

进一步说明:该xor约束使得分隔符在且仅当分隔符位于0和1的单元格之间(表示从未填充到填充或从填充到填充的变化)时才可以为1。因此,行或列中的一值分隔符将是该行或列中的块数的两倍。换句话说,给定行或列上的分隔符之和恰好是该行/列的大小的两倍。因此,存在以下约束:

for r,clue in enumerate(rowclues):
    prob += lpSum([rowseps[str(r+1)][c] for c in icols]) == 2 * clue,""
for c,clue in enumerate(colclues):
    prob += lpSum([colseps[r][str(c+1)] for r in irows]) == 2 * clue,""

就是这样。其余的仅要求默认求解器来求解ILP,然后在将生成的解决方案写入文件时对其进行格式化。


真的很好的答案。让我想了解LP解算器。您是否认为它可用于解决19x19、6色板的泛洪难题(链接)(关于计算解决方案的时间)?我已经回答了这个比赛(并赢得了比赛),但是我的方法(A *搜索算法)仅给出了非最优解。
tigrou

@tigrou谢谢。我不确定洪水问题是否足够线性,无法接受这样的解决方案。我当然看不到如何以这种方式建模。
quintopia '02

似乎有人已经尝试过:kunigami.blog/2012/09/16/flood-it-an-exact-approach但是,对于14x14的开发板,他们无法在可行的时间内优化解决方案。
tigrou

3

Java,6,093,092 4,332,656 3,637,260平方(最小),10,567,550 10,567,691 10,568,746平方(最大)

程序的两个变体都在源网格上重复执行操作,而不会改变幅度。

最小化器

收缩()

在此处输入图片说明

如果一个黑色正方形在90°角上有2个白色邻居和2个黑色邻居,则可以将其替换为白色正方形。

moveLine()

在此处输入图片说明 在此处输入图片说明

在类似上面的配置中,黑线可以移到右侧。沿顺时针和逆时针方向对所有4条线方向重复进行此操作,以开辟新的收缩可能性。

最大化器

取消注释该行,main()并为此版本注释掉其上方的行。

增长()

在此处输入图片说明

如果一个白色正方形在90°角上有2个白色邻居和2个黑色邻居,则可以将其替换为一个黑色正方形。

moveLine()

与最小化器相同。

资源

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.Base64;
import java.util.function.Function;

public class Main {
    private static final int SIZE = 16;
    private static final int SIZE_4 = SIZE + 4;
    private static final int E = 0;
    private static final int N = 1;
    private static final int W = 2;
    private static final int S = 3;

    private static final Base64.Decoder decoder = Base64.getMimeDecoder();
    private static final Base64.Encoder encoder = Base64.getMimeEncoder();
    private static int sourceBlack = 0;
    private static int targetBlack = 0;

    private static class Nonogram {
        private final boolean[] cells = new boolean[SIZE_4 * SIZE_4];
        private final int[] magnitudes;

        public Nonogram(String encoded) {
            super();
            byte[] decoded = decoder.decode(encoded);
            for (int i = 0; i < decoded.length; ++ i) {
                for (int j = 0; j < 8; ++ j) {
                    if ((decoded[i] & (1 << (7 - j))) != 0) {
                        int k = i * 8 + j;
                        cells[getPos(k / SIZE, k % SIZE)] = true;
                        ++ sourceBlack;
                    }
                }
            }
            magnitudes = calcMagnitudes();
        }

        private int getPos(int row, int col) {
            return (row + 2) * SIZE_4 + col + 2;
        }

        private int move(int pos, int dir, int count) {
            switch (dir) {
                case E: return pos + count;
                case N: return pos - count * SIZE_4;
                case W: return pos - count;
                case S: return pos + count * SIZE_4;
                default: return pos;
            }
        }

        private int move(int pos, int dir) {
            return move(pos, dir, 1);
        }

        private int[] calcMagnitudes() {
            int[] result = new int[SIZE * 2];
            for (int row = 0; row < SIZE; ++ row) {
                for (int col = 0; col < SIZE; ++ col) {
                    int pos = getPos(row, col);
                    if (cells[pos]) {
                        if (!cells[move(pos, W)]) {
                            ++ result[row + SIZE];
                        }
                        if (!cells[move(pos, N)]) {
                            ++ result[col];
                        }
                    }
                }
            }
            return result;
        }

        private boolean isBlack(int pos) {
            return cells[pos];
        }

        private boolean isWhite(int pos) {
            return !cells[pos];
        }

        private boolean allBlack(int pos, int dir, int count) {
            int p = pos;
            for (int i = 0; i < count; ++ i) {
                if (isWhite(p)) {
                    return false;
                }
                p = move(p, dir);
            }
            return true;
        }

        private boolean allWhite(int pos, int dir, int count) {
            int p = pos;
            for (int i = 0; i < count; ++ i) {
                if (isBlack(p)) {
                    return false;
                }
                p = move(p, dir);
            }
            return true;
        }

        private int findWhite(int pos, int dir) {
            int count = 0;
            int p = pos;
            while (cells[p]) {
                ++ count;
                p = move(p, dir);
            }
            return count;
        }

        @SafeVarargs
        private final void forEach(Function<Integer, Boolean>... processors) {
            outer:
            for (;;) {
                for (Function<Integer, Boolean> processor : processors) {
                    for (int row = 0; row < SIZE; ++ row) {
                        for (int col = 0; col < SIZE; ++ col) {
                            if (processor.apply(getPos(row, col))) {
                                continue outer;
                            }
                        }
                    }
                }
                return;
            }
        }

        private boolean shrink(int pos) {
            if (cells[pos] && cells[move(pos, W)] != cells[move(pos, E)] &&
                    cells[move(pos, N)] != cells[move(pos, S)]) {
                cells[pos] = false;
                return true;
            }
            return false;
        }

        private boolean grow(int pos) {
            if (!cells[pos] && cells[move(pos, W)] != cells[move(pos, E)] &&
                    cells[move(pos, N)] != cells[move(pos, S)]) {
                cells[pos] = true;
                return true;
            }
            return false;
        }

        private boolean moveLine(boolean clockwise, int dir, int sourcePos) {
            int from = (dir + (clockwise ? 1 : 3)) % 4;
            int to = (dir + (clockwise ? 3 : 1)) % 4;
            int opp = (dir + 2) % 4;
            if (isBlack(sourcePos) && isWhite(move(sourcePos, from)) && isWhite(move(sourcePos, dir))) {
                int toCount = findWhite(move(move(sourcePos, dir), to), to) + 1;
                if (allWhite(move(sourcePos, to), to, toCount + 1)) {
                    int lineCount = 1;
                    int tmpPos = move(sourcePos, opp);
                    while (isBlack(tmpPos) && isWhite(move(tmpPos, from)) && allWhite(move(tmpPos, to),  to, toCount + 1)) {
                        ++ lineCount;
                        tmpPos = move(tmpPos, opp);
                    }
                    if (allBlack(tmpPos, to, toCount + 1)) {
                        tmpPos = sourcePos;
                        for (int j = 0; j < lineCount; ++ j) {
                            cells[tmpPos] = false;
                            cells[move(tmpPos, to, toCount)] = true;
                            tmpPos = move(tmpPos, opp);
                        }
                        return true;
                    }
                }
            }
            return false;
        }

        public Nonogram minimize() {
            for (int i = 0; i < 5; ++ i) {
                forEach(pos -> shrink(pos), pos -> moveLine(true, E, pos), pos -> moveLine(true, N, pos),
                        pos -> moveLine(true, W, pos), pos -> moveLine(true, S, pos));
                forEach(pos -> shrink(pos), pos -> moveLine(false, E, pos), pos -> moveLine(false, N, pos),
                        pos -> moveLine(false, W, pos), pos -> moveLine(false, S, pos));
            }
            return this;
        }

        public Nonogram maximize() {
            for (int i = 0; i < 5; ++ i) {
                forEach(pos -> grow(pos), pos -> moveLine(true, E, pos), pos -> moveLine(true, N, pos),
                        pos -> moveLine(true, W, pos), pos -> moveLine(true, S, pos));
                forEach(pos -> grow(pos), pos -> moveLine(false, E, pos), pos -> moveLine(false, N, pos),
                        pos -> moveLine(false, W, pos), pos -> moveLine(false, S, pos));
            }
            return this;
        }

        public String toBase64() {
            if (!Arrays.equals(magnitudes, calcMagnitudes())) {
                throw new RuntimeException("Something went wrong!");
            }
            byte[] decoded = new byte[SIZE * SIZE / 8];
            for (int i = 0; i < decoded.length; ++ i) {
                for (int j = 0; j < 8; ++ j) {
                    int k = i * 8 + j;
                    if (cells[getPos(k / SIZE, k % SIZE)]) {
                        decoded[i] |= 1 << (7 - j);
                        ++ targetBlack;
                    }
                }
            }
            return encoder.encodeToString(decoded);
        }

        @Override
        public String toString() {
            StringBuilder b = new StringBuilder();
            for (int row = 0; row < SIZE; ++ row) {
                for (int col = 0; col < SIZE; ++ col) {
                    b.append(cells[getPos(row, col)] ? '#' : ' ');
                }
                b.append('\n');
            }
            return b.toString();
        }
    }

    public static void main(String[] args) throws Exception {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter("solutions_b64.txt"));
                BufferedReader reader = new BufferedReader(new FileReader("nonograms_b64.txt"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                writer.write(new Nonogram(line).minimize().toBase64() + "\n");
                //writer.write(new Nonogram(line).maximize().toBase64() + "\n");
            }
        }
        System.out.printf("%d -> %d", sourceBlack, targetBlack);
    }
}

1

重击-10,239,288平方

作为最后的参考解决方案:

cp nonograms_b64.txt solutions_b64.txt

由于如果找不到更好的解决方案,则允许您的程序返回相同的网格,因此逐字打印整个文件也是有效的。

测试文件中总共有10,239,288个黑色正方形,非常接近您希望从50,000个网格(每个256个正方形)中填充80%的正方形所期望的10,240,000个黑色正方形。与测试电池问题一样,我选择了一些测试用例,期望最佳分数在200万左右,尽管我怀疑这次的分数将接近4或500万。


如果任何人都可以创建一个最大化黑色正方形而不是最小化黑色正方形并设法获得超过10,240,000 的解决方案,那么我可能会考虑给予赏金。


1

Matlab,7,270,894平方英尺(约占原始面积的71%)

想法是一个简单的重复贪婪搜索:对于每个黑色正方形,请尝试是否将其设置为白色而不更改非地理大小。重复两次。(您可以通过多次重复来获得更好的结果,但不是免费的:它会相应地延长运行时间。现在大约是80分钟。如果我们不必计算所有50k测试用例,我会这样做。)

此处是代码(每个函数通常都在单独的文件中)。

function D = b64decode(E)
% accepts a string of base 64 encoded data, and returns a array of zeros
% and ones
F = E;
assert( mod(numel(E),4)==0 && 0 <= sum(E=='=') && sum(E=='=') <= 2,'flawed base 64 code')

F('A' <= E & E<= 'Z') = F('A' <= E & E<= 'Z') -'A';       %upper case
F('a' <= E & E<= 'z') = F('a' <= E & E<= 'z') -'a' + 26;  %lower case
F('0'<= E & E <= '9') = F('0'<= E & E <= '9') -'0' + 52;  %digits
F( E == '+') = 62;
F( E == '/') = 63;
F( E == '=') = 0;

D=zeros(1,numel(E)*3*8/4);

for k=1:numel(E);
    D(6*(k-1)+1 + (0:5)) = dec2bin(F(k),6)-'0';
end

if E(end) == '=';
    D(end-7:end) = [];
    if E(end-1) == '=';
        D(end-7:end) = [];
    end
end
end

function E = b64encode(D)
assert(mod(numel(D),8)==0,'flawed byte code');
N=0;
while mod(numel(D),6) ~= 0;
    D = [D,zeros(1,8)];
    N = N+1;
end
dict = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

E=zeros(1,numel(D)/6)+'=';
for k=0:numel(E)-N-1;
    E(k+1) = dict(bin2dec( [D(6*k+(1:6))+'0',''] ) + 1);
end

E = [E,''];
end


function [B,T,O] = reduce_greedy(N)
% reduce greedily
NM = nomographic_magnitude(N);
B = N; %current best
M = N;
T = nnz(N); %current number of filled squares
O = T;

for r=1:2;  %how many repetitions
    I = find(B);
    for k=1:numel(I);
        M = B;
        M( I(k) ) = 0;
        %check whether we have a valid solution
        if all(NM == nomographic_magnitude(M))
            if T > nnz(M); %did we actually reduce the number of filled squares?
                B = M;
                T = nnz(M);
            end
        end
    end
end


%% main file
string_in = fileread('nonograms_b64.txt');
string_out = string_in;
tic
total_new = 0;  %total number of black squares
total_old = 0;
M = 50000;
for k=1:M;
    disp(k/M); %display the progress
    line = string_in(45*(k-1)+(1:44));
    decoded = b64decode(line);        
    nonogram = reshape(decoded,16,[]) ;%store nonogram as a matrix
    [B,T,O] = reduce_greedy(nonogram);
    disp([nnz(B),nnz(nonogram)])
    total_new = total_new + T;
    total_old = total_old + O;
    string_in(45*(k-1)+(1:44)) = b64encode(B(:).');
end
toc
F = fopen('nonograms_b64_out.txt','w');
fprintf(F,string_out);
%%
disp([total_new,total_old])
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.