Нонограмма - это японская игра-головоломка, цель которой - нарисовать черно-белое изображение в соответствии со списком смежных регионов, например:
Определите неографическую величину строки или столбца как число смежных черных областей в этой строке или столбце. Например, верхняя строка имеет неографическую величину 1, потому что в этой строке есть одна область из 2 квадратов. Восьмой ряд имеет неографическую величину 3, потому что он имеет 2, 2, 1.
Пустая строка или столбец имеют неографическую величину 0.
Ваша задача - написать программу, которая принимает сетку решений для неграммы и создает сетку решений с наименьшим количеством заполненных квадратов. меньшим где каждая строка и столбец имеют такой же неографический размер, как и данная сетка решений.
Например, сетка неграммы со всеми заполненными квадратами имеет неографическую величину 1 в каждой строке или столбце:
Та же неографическая величина могла бы быть достигнута, если бы диагональная полоса проходила через сетку, значительно уменьшая количество заполненных квадратов:
Ваша программа получит вход, состоящий из 50000 строк из этого файла (1,32 МБ текстового файла tar.gz; 2,15 МБ разархивированного файла), каждая из которых представляет собой отдельную сетку решения неграммы 16 × 16 со случайно заполненными квадратами (80% черного цвета), и выведите еще 50000 строк, каждая из которых содержит сетку оптимизированного решения для соответствующей входной сетки.
Каждая сетка представлена в виде строки base64 с 43 символами (квадраты кодируются слева направо, затем сверху вниз), и ваша программа должна будет возвращать свои выходные данные в том же формате. Например, первая сетка в файле выглядит E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=
так:
Сетка начинается с E
какой карты 000100
, поэтому первые шесть ячеек в верхнем ряду все белые, кроме четвертой. Следующий символ - это то, на /
что отображаются 111111
следующие 6 ячеек, все черные - и так далее.
Ваша программа должна на самом деле возвращать сетку решений с правильными неографическими значениями для каждого из 50 000 тестовых случаев. Разрешается возвращать ту же сетку, что и входные данные, если ничего лучшего не было найдено.
Программа для возврата наименьшего количества заполненных квадратов (на любом языке) является победителем, с более коротким кодом, являющимся таймбрейкером.
Текущее табло:
- 3,637,260 - Sleafar, Java
- 7,270,894 - flawr, Matlab
- 10,239,288 - Джо З., Баш
источник
Ответы:
Python 2 & PuLP - 2 644 688 квадратов (оптимально минимизированы); 10 753 553 квадрата (оптимально максимизированы)
Минимально гольф до 1152 байтов
(Примечание: строки с сильными отступами начинаются с табуляции, а не пробелов.)
Пример вывода: https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing
Оказывается, что подобные проблемы легко конвертируются в целочисленные линейные программы, и мне нужна была основная проблема, чтобы научиться использовать PuLP - интерфейс Python для различных программ для решения LP - для моего собственного проекта. Также оказалось, что PuLP чрезвычайно прост в использовании, и неумелый сборщик LP сработал отлично, когда я впервые попробовал его.
Две полезные вещи, связанные с использованием IP-решателя с ветвями и связями для выполнения тяжелой работы по решению этой проблемы для меня (за исключением того, что нет необходимости реализовывать решатель ветвей и границ):
Как использовать эту программу
Сначала вам нужно установить PuLP.
pip install pulp
должен сделать трюк, если у вас установлен пипс.Затем вам нужно поместить в файл с именем «c» следующее: https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing
Затем запустите эту программу в любой поздней сборке Python 2 из того же каталога. Менее чем через день у вас будет файл с именем «s», содержащий 50 000 решенных неограммных сеток (в читаемом формате), в каждой из которых будет указано общее количество заполненных квадратов.
Если вы хотите максимизировать количество заполненных квадратов вместо этого, измените
LpMinimize
на строке 8LpMaximize
вместо этого. Вы получите очень похожий результат: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharingФормат ввода
Эта программа использует измененный формат ввода, поскольку Джо З. сказал, что нам будет разрешено перекодировать формат ввода, если мы захотим в комментарии к ОП. Нажмите на ссылку выше, чтобы увидеть, как это выглядит. Он состоит из 10000 строк, каждая из которых содержит 16 цифр. Строки с четными номерами - это величины для строк данного экземпляра, а строки с нечетными номерами - это величины для столбцов того же экземпляра, что и линия над ними. Этот файл был сгенерирован следующей программой:
(Эта программа перекодирования также дала мне дополнительную возможность протестировать мой собственный класс BitQueue, который я создал для того же проекта, упомянутого выше. Это просто очередь, в которую данные могут передаваться как последовательности битов ИЛИ байтов, и из которых данные могут быть вытолкнутым либо битом, либо байтом за раз. В этом случае это работало отлично.)
Я перекодировал входные данные по той конкретной причине, что для построения ILP дополнительная информация о сетках, которые использовались для генерации величин, совершенно бесполезна. Величины - единственные ограничения, и поэтому величины - это все, к чему мне нужен был доступ.
Ungolfed ILP строитель
Это программа, которая фактически создала «пример вывода», связанный выше. Отсюда и очень длинные струны в конце каждой сетки, которые я обрезал при игре в гольф. (Версия для гольфа должна давать идентичный результат, без слов
"Filled squares for "
)Как это устроено
Я использую сетку 18x18, с центральной частью 16x16, которая является настоящим решением головоломки.
cells
это сетка. Первая строка создает 324 двоичных переменных: «cell_0_0», «cell_0_1» и т. Д. Я также создаю сетки "пробелов" между и вокруг ячеек в части решения сетки.rowseps
указывает на 289 переменных, которые символизируют пространства, которые разделяют ячейки по горизонтали, аcolseps
также указывает на переменные, которые отмечают пространства, которые разделяют ячейки по вертикали. Вот диаграмма Unicode:В
0
ы и□
с являются двоичные значения отслеживаемыхcell
переменных, то|
s являются двоичные значения отслеживали с помощьюrowsep
переменных, и-
s являются двоичные значения отслеживали с помощьюcolsep
переменных.Это целевая функция. Просто сумма всех
cell
переменных. Поскольку это бинарные переменные, это как раз количество заполненных квадратов в решении.Это просто устанавливает ячейки вокруг внешнего края сетки в ноль (вот почему я представлял их как нули выше). Это наиболее целесообразный способ отследить, сколько «блоков» ячеек заполнено, поскольку он гарантирует, что каждому изменению с незаполненного на заполненный (перемещение по столбцу или строке) соответствует соответствующее изменение с заполненного на незаполненный (и наоборот). ), даже если первая или последняя ячейка в строке заполнена. Это единственная причина использования сетки 18x18. Это не единственный способ подсчета блоков, но я думаю, что это самый простой.
Это настоящее мясо логики ILP. В основном это требует, чтобы каждая ячейка (кроме тех, которые находятся в первой строке и столбце) была логическим xor ячейки и разделителя непосредственно слева от ее строки и непосредственно над ней в ее столбце. Я получил ограничения, которые имитируют xor в целочисленной программе {0,1} из этого замечательного ответа: /cs//a/12118/44289
Чтобы объяснить немного больше: это ограничение xor делает так, чтобы разделители могли быть 1, если и только если они лежат между ячейками, которые являются 0 и 1 (отмечая изменение от незаполненного к заполненному или наоборот). Таким образом, в строке или столбце будет ровно вдвое больше однозначных разделителей, чем количество блоков в этой строке или столбце. Другими словами, сумма разделителей в данной строке или столбце ровно в два раза больше этой строки / столбца. Отсюда следующие ограничения:
И это в значительной степени так. Остальные просто запрашивают решение по умолчанию для решения ILP, а затем форматируют полученное решение, когда оно записывает его в файл.
источник
Ява,
609309243266563637260 квадратов (минимизировано),105675501056769110568746 квадратов (максимально)Оба варианта программы многократно выполняют операции с исходной сеткой, не меняя величины.
Minimizer
сокращаться, сжиматься()
Если черный квадрат имеет 2 белых соседа и 2 черных соседа под углом 90 °, его можно заменить белым квадратом.
moveLine ()
В конфигурациях, подобных выше, черная линия может быть перемещена вправо. Это делается повторно для всех четырех направлений линии по часовой стрелке и против часовой стрелки, чтобы открыть новые возможности сжатия.
Maximizer
Раскомментируйте строку
main()
и закомментируйте строку над ней для этой версии.расти ()
Если белый квадрат имеет 2 белых соседа и 2 черных соседа под углом 90 °, его можно заменить черным квадратом.
moveLine ()
Так же, как в минимизаторе.
Источник
источник
Баш - 10 239 288 квадратов
В качестве справочного решения последнего места:
Поскольку вашей программе разрешено возвращать ту же сетку, если она не может найти лучшего решения, распечатка всего файла дословно также допустима.
Всего в тестовом файле находится 10 239 288 черных квадратов, что довольно близко к 10 240 000 000, которые можно ожидать от 80% квадратов, заполненных из 50 000 сеток по 256 квадратов в каждом. Как обычно с моими вопросами о тестовых батареях, я выбрал количество тестовых случаев с ожиданием того, что оптимальные оценки будут около 2 миллионов, хотя я подозреваю, что на этот раз оценки будут ближе к 4 или 5 миллионам ,
Если кто-то может создать решение, которое максимизирует черные квадраты, а не минимизирует их и сможет получить более 10 240 000, я мог бы рассмотреть вопрос о предоставлении ему награды.
источник
Matlab, 7 270 894 квадрата (~ 71% оригинала)
Идея - это простой повторный жадный поиск: для каждого черного квадрата попробуйте, если вы можете установить его на белый, не меняя неографических величин. Повторите это дважды. (Вы могли бы добиться лучших результатов с большим количеством повторений, но не бесплатно: это приводит к соответственно более длительному времени выполнения. Теперь это было около 80 минут. Я сделал бы это, если бы нам не пришлось вычислять все тестовые случаи 50k ...)
Здесь код (каждая функция в отдельном файле, как обычно.)
источник