задача
Напишите программу, которая читает три целых числа m , n либо из STDIN, либо в качестве аргументов командной строки, печатает все возможные наклоны прямоугольника с размерами m × n с помощью домино 2 × 1 и 1 × 2 и, наконец, количество допустимых значений.
Домино отдельных листов должны быть представлены двумя штрихами ( -
) для 2 × 1 и двумя вертикальными чертами ( |
) для 1 × 2 домино. После каждого тайлинга (включая последний) должен следовать перевод строки.
В целях оценки вы также должны принять флаг из STDIN или в качестве аргумента командной строки, который заставляет вашу программу печатать только количество допустимых значений, но не сами значения.
Ваша программа не может быть длиннее 1024 байта. Он должен работать для всех входов, таких что m × n ≤ 64 .
(Вдохновлено печатью всех элементов домино прямоугольника 4x6 .)
пример
$ sdt 4 2
----
----
||--
||--
|--|
|--|
--||
--||
||||
||||
5
$ sdt 4 2 scoring
5
счет
Ваша оценка определяется временем выполнения вашей программы для ввода 8 8 с установленным флагом.
Чтобы сделать этот код быстрейшим, а не самым быстрым компьютерным испытанием, я буду запускать все представления на своем собственном компьютере (Intel Core i7-3770, 16 ГБ ОЗУ PC3-12800), чтобы определить официальный результат.
Пожалуйста, оставьте подробные инструкции о том, как скомпилировать и / или выполнить ваш код. Если вам требуется конкретная версия компилятора / интерпретатора вашего языка, сделайте заявление об этом.
Я оставляю за собой право оставлять материалы без оценки, если:
Для моей операционной системы нет бесплатного (как в пиве) компилятора / интерпретатора (Fedora 21, 64 бита).
Несмотря на наши усилия, ваш код не работает и / или выдает неправильные результаты на моем компьютере.
Компиляция или выполнение занимает больше часа.
Ваш код или единственный доступный компилятор / интерпретатор содержат системный вызов
rm -rf ~
или что-то такое же подозрительное.
Leaderboard
Я переоценил все представления, выполняя как компиляции, так и выполнения в цикле с 10 000 итераций для компиляции и от 100 до 10000 итераций для выполнения (в зависимости от скорости кода) и вычисления среднего значения.
Это были результаты:
User Compiler Score Approach
jimmy23013 GCC (-O0) 46.11 ms = 1.46 ms + 44.65 ms O(m*n*2^n) algorithm.
steveverrill GCC (-O0) 51.76 ms = 5.09 ms + 46.67 ms Enumeration over 8 x 4.
jimmy23013 GCC (-O1) 208.99 ms = 150.18 ms + 58.81 ms Enumeration over 8 x 8.
Reto Koradi GCC (-O2) 271.38 ms = 214.85 ms + 56.53 ms Enumeration over 8 x 8.
источник
--
. Если это вертикально, это два|
, один под другим.Ответы:
С
Простая реализация ...
Читерская версия
Объяснение более быстрого алгоритма
Сканирует слева направо и сохраняет состояние
d[i][j]
, в котором:i
находится в[0,m)
, что означает текущий столбец.j
битовый вектор размераn
, где бит будет равен 1, если соответствующая позиция в столбцеi
уже занята до начала работы с этим столбцом. Т.е. он занят правой половиной--
.d[i][j]
общее количество различных элементов мозаичного изображения.Затем скажите
e[i][j]
= сумма,d[i][k]
где вы можете положить вертикальную основу домино,k
чтобы сформироватьj
.e[i][j]
будет количеством углов, где каждый 1 битj
занят чем-либо, кроме левой половины a--
. Заполните их,--
и вы получитеd[i+1][~j]
=e[i][j]
.e[m-1][every bit being 1]
илиd[m][0]
это окончательный ответ.Наивная реализация даст вам временную сложность где-то рядом
g[n]=2*g[n-1]+g[n-2] = O((sqrt(2)+1)^n)
(уже достаточно быстро, если n = m = 8). Но вместо этого вы можете сначала выполнить цикл для каждого возможного домино и попытаться добавить его в каждый лист, в который можно добавить это домино, и объединить результат с исходным массивомd
(как алгоритм для задачи о рюкзаке). И это становится O (n * 2 ^ n). А все остальное - детали реализации. Весь код работает в O (m * n * 2 ^ n).источник
-O1
кажется, самое приятное место. Я перепробовал все уровни оптимизации.)С
После раунда оптимизаций, адаптированных к измененным правилам:
Я начал сталкиваться с ограничением длины в 1024 символа, поэтому мне пришлось несколько снизить читабельность. Значительно более короткие имена переменных и т. Д.
Инструкции по сборке:
Запустить с включенным выводом решения:
Запустить только с подсчетом решений:
Некоторые комментарии:
-O2
кажется, что все хорошо.Также обратите внимание, что код все еще генерирует реальные решения, даже в режиме «только подсчет». Всякий раз, когда решение найдено,
vM
битовая маска содержит1
для позиций с вертикальной чертой и0
для позиций с горизонтальной чертой. Пропускается только преобразование этой битовой маски в формат ASCII и фактический вывод.источник
-O2
должно быть хорошо.-O2
кажется, самое приятное место. Я перепробовал все уровни оптимизации.)С
Идея состоит в том, чтобы сначала найти все возможные варианты расположения горизонтальных домино в ряд, сохранить их,
r[]
а затем упорядочить, чтобы получить все возможные варианты расположения вертикальных домино.Код для позиционирования горизонтального домино в ряд изменен из моего ответа: https://codegolf.stackexchange.com/a/37888/15599 . Это медленно для более широких сеток, но это не проблема для случая 8x8.
Инновация заключается в способе сборки рядов. Если у платы нечетное количество строк, то при разборе ввода она поворачивается на 90 градусов, поэтому теперь она имеет четное количество строк. Теперь я размещаю несколько вертикальных домино по центральной линии. Из-за симметрии, если есть
c
способы расположить оставшиеся домино в нижней половине, также должны бытьc
способы расположить оставшиеся домино в верхней половине, что означает, что для данного расположения вертикальных домино на центральной линии, естьc*c
возможные решения , Поэтому анализируется только центральная линия плюс половина платы, когда программе требуется распечатать только количество решений.f()
строит таблицу возможных расположений горизонтальных домино и просматривает возможные размещения вертикальных домино на центральной линии. Затем он вызывает рекурсивную функцию,g()
которая заполняет строки. Если требуется печать, для этого вызывается функцияh()
.g()
вызывается с 3 параметрами.y
текущая строка иd
направление (вверх или вниз), в котором мы заполняем доску от центра наружу.x
содержит растровое изображение, обозначающее вертикальные домино, которые являются неполными из предыдущего ряда. Все возможные размещения домино в ряд от r [] перепробованы. В этом массиве 1 представляет вертикальное домино, а пара нулей - горизонтальное домино. Действительный элемент массива должен иметь по крайней мере , достаточно 1, чтобы закончить все незавершенные вертикальные домино с последнего ряда:(x&r[j])==x
. У него может быть больше 1, что указывает на то, что начинаются новые вертикальные домино. Тогда для следующей строки нам нужны только новые домино, поэтому мы снова вызываем процедуру с помощьюx^r[j]
.Если достигнут конечный ряд и нет незавершенных вертикальных домино, висящих сверху или снизу доски,
x^r[j]==0
то половина была успешно завершена. Если мы не печатаем, достаточно заполнить нижнюю половину и использоватьc*c
для выработки общего количества аранжировок. Если мы печатаем, необходимо также заполнить верхнюю половину и затем вызвать функцию печатиh()
.КОД
Обратите внимание, что ввод с нечетным числом строк и четным числом столбцов поворачивается на 90 градусов в фазе анализа. Если это неприемлемо, функцию печати
h()
можно изменить в соответствии с ней. (РЕДАКТИРОВАТЬ: не требуется, см. Комментарии.)РЕДАКТИРОВАТЬ: новая функция
e()
была использована для проверки четностиi
(т.е. количество домино, охватывающих центральную линию.) Четностьi
(количество полдомино на центральной линии, выступающей в каждую половину доски) должно быть таким же, как нечетность общего количества пробелов в каждой половине (определяемых какn/2
), потому что только тогда домино может заполнить все доступное пространство. Это редактирование устраняет половину значений i и, следовательно, делает мою программу примерно в два раза быстрее.источник
-O0
было приятное место для итога. Другие варианты замедлили компиляцию.)32 2 s
. Я остановил это примерно через 15 минут.2 32 s
работает почти мгновенно. Сканирование всех возможных вертикальных домино чрезвычайно расточительно для этогоH=2
случая, потому что на самом деле у нас уже есть вся необходимая информацияr[]
. Я очень доволен официальным временем для8 8 s
Вот пример патча для случая, о котором вы упомянули:if(H==2){C=p;if(!S)for(i=0;i<p;i++)q[0]=q[1]=r[i],h();}else for(i=0;i<=k;i++)c=0,g(H/2,1,i),C+=c*c;
Как вы можете видеть, этот фрагмент будет работать мгновенноH=2
с установленным флагом. Общее время выполнения ограничено тем, что здание,r[]
безусловно, имеет место для улучшения.if(t)for(a=n;a--;a%H||puts(""))putchar(124-(q[a%H]>>a/H)%2*79);else for(a=n;a--;a%W||puts(""))putchar(45+(q[a/W]>>a%W)%2*79);
длина кода все еще значительно ниже 1000 байтов, и влияние на время компиляции должно быть минимальным. Я не включал эти патчи прошлой ночью, так как был слишком уставшим.