Двоичная строка является строкой , которая содержит только символы , взятые из 01 . Сбалансирован двоичная строка является двоичной строкой , которая содержит ровно столько 0 сек , как 1 с.
Вам дается положительное целое число n и произвольное количество масок, каждая из которых имеет длину 2n символов и содержит только символы, взятые из 012 . Двоичная строка и маска совпадают, если они имеют одинаковую длину и согласуются с символом в каждой позиции, где маска не имеет 2 . Например , маска 011022 спичек бинарные струн 011000 , 011001 , 011010 , 011011 .
Учитывая n и маски в качестве входных данных (разделенных символами новой строки), вы должны вывести количество различных сбалансированных двоичных строк, которые соответствуют одной или нескольким маскам.
Примеры
вход
3
111222
000112
122020
122210
102120
аргументация
- Единственная сбалансированная двоичная строка, совпадающая с 111222, это 111000 .
- Единственная сбалансированная двоичная строка, совпадающая с 000112, это 000111 .
- Сбалансированные двоичные строки , соответствующие 122020 являются 111000 (уже подсчитан), 110010 и 101010 .
- Сбалансированные двоичные строки, соответствующие 122210 : 110010 (уже посчитано), 101010 (уже посчитано) и 100110 .
- Сбалансированные двоичные строки, соответствующие 102120, равны 101100 и 100110 (уже учтены).
Таким образом, вывод должен быть
6
вход
10
22222222222222222222
аргументация
- Есть 20 выбрать 10 сбалансированных двоичных строк длиной 20.
Вывод
184756
победитель
Победителем будет тот, кто посчитает входные данные соревнования быстрее всего, конечно же, обработав их так же, как и любой другой вход. (Я использую определенный код, чтобы иметь явного победителя и избегать случаев, когда разные входные данные давали бы разных победителей. Если вы думаете о лучшем способе найти самый быстрый код, скажите мне об этом).
Конкурсный вклад
источник
Ответы:
С
Если вы не работаете в Linux или у вас другие проблемы с компиляцией, вам, вероятно, следует удалить код синхронизации (
clock_gettime
).Примеры случаев:
(Время для процессора i7-4770K с частотой 4,1 ГГц.) Будьте осторожны,
testcase-hard
используйте около 3-4 ГБ памяти.Это в значительной степени реализация метода включения-исключения, разработанного blutorange, но сделанного так, что он будет обрабатывать пересечения любой глубины.
Написанный код тратит много времени на выделение памяти и станет еще быстрее, когда я оптимизирую управление памятью.Я сэкономил около 25%
testcase-hard
, но производительность оригинала (testcase-long
) практически не изменилась, так как там не происходит большого выделения памяти. Я собираюсь настроиться немного больше, прежде чем позвонить: думаю, я тоже смогу добиться улучшения на 25-50%testcase-long
.Mathematica
Как только я заметил, что это проблема #SAT, я понял, что могу использовать встроенное в Mathematica
SatisfiabilityCount
:Вывод:
Это 298 208 861 472 маски за 1,3 секунды (i7-3517U @ 1,9 ГГц), включая время, потраченное на загрузку тестового примера из pastebin.
источник
testcase-hard
может быть выполнен очень быстро, если ваш код ищет маски, которые можно комбинировать. Если ваш код делает это, то удалите все остальные строки (так что/^2*02*$/
остаются только случаи). Я не думаю, что этот случай можно оптимизировать.Рубин, довольно быстро, но это зависит от ввода
Теперь ускорение в 2 ~ 2,5 раза происходит за счет перехода от строк к целым числам.
Применение:
Например.
Количество совпадений для одной маски легко рассчитывается по биномиальному коэффициенту. Так, например,
122020
необходимо 32
с заполнены, 10
и 21
. Таким образом, естьnCr(3,2)=nCr(3,1)=3!/(2!*1!)=3
разные двоичные строки, соответствующие этой маске.Пересечение между n масками m_1, m_2, ... m_n является маской q, так что двоичная строка s соответствует q только тогда, когда она соответствует всем маскам m_i.
Если взять две маски m_1 и m_2, их пересечение легко вычисляется. Просто установите m_1 [i] = m_2 [i], если m_1 [i] == 2. Пересечение между
122020
и111222
является111020
:Две отдельные маски соответствуют 3 + 1 = 4 строкам, маска пересечения соответствует одной строке, таким образом, есть 3 + 1-1 = 3 уникальных строки, соответствующих одной или обеим маскам.
Я назову N (m_1, m_2, ...) количество строк, соответствующих всем m_i. Применяя ту же логику, что и выше, мы можем вычислить количество уникальных строк, сопоставленных хотя бы с одной маской, заданной принципом исключения включения, и также см. Ниже, например, так:
Есть много, много, много комбинаций взятия, скажем, 30 масок из 200.
Таким образом, это решение предполагает, что существует не так много пересечений высших порядков входных масок, т.е. большинство n-кортежей с n> 2 масками не будут иметь общих совпадений.
Используйте код здесь, код в ideone может быть устаревшим.
Я добавил функцию,
remove_duplicates
которую можно использовать для предварительной обработки ввода и удаления масокm_i
, чтобы все соответствующие ему строки также соответствовали другой маскеm_j
. Для текущего ввода это на самом деле занимает больше времени, поскольку таких масок нет (или их не много). , так что функция не применяется к данным еще в коде ниже.Код:
Это называется принципом исключения включения, но до того, как кто-то указал мне на это, у меня было собственное доказательство, так что вот так. Делать что-то самостоятельно - это здорово.
Давайте рассмотрим случай 2 масок, звоните потом
0
и1
, во-первых. Мы берем каждую сбалансированную двоичную строку и классифицируем ее в зависимости от того, к какой маске она подходит.c0
Число тех , которые соответствуют лишь маскируют0
,c1
Nunber из тех , которые соответствуют только1
,c01
те , что маски матч0
и1
.Позвольте
s0
быть число сумма количества совпадений для каждой маски (они могут перекрываться). Позвольтеs1
быть сумма количества совпадений для каждой пары (2-комбинация) масок. Позвольтеs_i
быть сумма количества совпадений для каждой (я + 1) комбинации масок. Количество совпадений n-масок - это количество двоичных строк, соответствующих всем маскам.Если существует n масок, желаемый результат является суммой всех
c
, т.е.c = c0+...+cn+c01+c02+...+c(n-2)(n-1)+c012+...+c(n-3)(n-2)(n-1)+...+c0123...(n-2)(n-1)
, То, что программа вычисляет, является чередующейся суммой всехs
, т.е.s = s_0-s_1+s_2-+...+-s_(n-1)
, Мы хотим доказать этоs==c
.п = 1 очевидно. Рассмотрим n = 2. Подсчет всех совпадений по маске
0
даетc0+c01
(количество строк, совпадающих только с 0 + совпадающих с обоими0
и1
), подсчитывая все совпадения по1
маскамc1+c02
. Мы можем проиллюстрировать это следующим образом:По определению
s0 = c0 + c1 + c12
.s1
сумма совпадений каждой 2-комбинации[0,1]
, т.е. все uniqyec_ij
с. Имейте это в видуc01=c10
.Таким образом,
s=c
для n = 2.Теперь рассмотрим n = 3.
Таким образом,
s=c
для n = 3.c__i
представляет всеc
s с (i + 1) индексами, например,c__1 = c01
для n = 2 иc__1 = c01 + c02 + c12
для n == 3.Для n = 4 шаблон начинает появляться:
Таким образом,
s==c
для n = 4.В общем, мы получаем биномиальные коэффициенты, подобные этому (↓ это i, → это j):
Чтобы увидеть это, рассмотрим, что для некоторых
i
иj
есть:Как это может показаться запутанным, вот определение, примененное к примеру. Для i = 1, j = 2, n = 4 это выглядит так (см. Выше):
Так что здесь x = 6 (01, 02, 03, 12, 13, 23), y = 2 (два c с тремя индексами для каждой комбинации), z = 4 (c012, c013, c023, c123).
В общем, есть
x*y
коэффициентыc
с (j + 1) индексами, и естьz
разные, поэтому каждыйx*y/z
раз встречается , что мы называем коэффициентомk_ij
. По простой алгебре получаемk_ij = ncr(n,i+1) ncr(n-i-1,j-i) / ncr(n,j+1) = ncr(j+1,i+1)
.Таким образом, индекс дается выражением:
k_ij = nCr(j+1,i+1)
Если вы помните все определения, все, что нам нужно показать, это то, что переменная сумма каждого столбца дает 1.Таким образом, переменная сумма
s0 - s1 + s2 - s3 +- ... +- s(n-1)
может быть выражена как:Таким образом,
s=c
для всех n = 1,2,3, ...источник
0011 < 2211
,0022 < 0222
. Я думаю, что это делает группы не больше2*n
, хотя, в худшем случае, все равно слишком велико.unifying two masks
этом терминеunion
имеет смысл для меня, так и я могу определить это так, но вы правы, в интересах взаимопонимания, которое я провел. @ Agawa001 Можете ли вы быть более конкретным? Кроме того, если у вас есть хорошая идея, чтобы сделать это быстрее, не стесняйтесь использовать любые идеи из этого ответа для вашей программы / ответа. На данный момент, это достаточно быстро для большого тестового примера, и если оно сделано многопоточным, оно должно занять <0,1 с, что ниже любого значимого измерения / сравнения, поэтому для более сложных тестовых случаев необходимы.С
Удачи в получении большого вклада для этого - вероятно, потребуется около ночи, чтобы пройти ок. 60 ^ 30 перестановок! Может быть, набор данных промежуточного размера может быть хорошей идеей?
источник