Учитывая целое число 2n, найдите количество возможных способов, которыми 2n ^ 2 черных пешек и 2n ^ 2 белых пешек могут быть размещены на шахматной доске 2n на 2n таким образом, чтобы никакая пешка не атаковала другую.
- Черная пешка может атаковать только белую пешку, и наоборот.
- Следуют обычным шахматным правилам атаки: белые пешки атакуют квадраты сразу по диагонали спереди, а черные пешки атакуют квадраты сразу по диагонали назад (как заметил белый наблюдатель).
- Все вращения, отражения считаются отличимыми.
Программа, которая может выводить все возможные конфигурации для наибольшего значения 2n за 120 секунд, побеждает. (Все программы приветствуются)
Например, программа Алисы может обрабатывать до n = 16 в течение 120 секунд, в то время как Боб может обрабатывать до n = 20 в течение того же времени. Боб выигрывает.
Платформа: Linux 2,7 ГГц @ 4 процессора
fastest-code
combinatorics
chess
Агнишом Чаттопадхяй
источник
источник
2n^2
пешки, это(2n)^2
или2(n^2)
?Ответы:
Java, n = 87 на моей машине
Результат для n = 87
В настоящее время используется схема динамического программирования, в которой для вычисления способов размещения
p
пешек на квадратах одного цвета используются O (n ^ 4) операций0 <= p <= n^2
. Я думаю, что должно быть возможно сделать это намного более эффективно.Проверьте результаты здесь.
объяснение
В правильном решении самые нижние белые пешки в каждом столбце должны образовывать зигзагообразную линию, например:
То есть высота строки в столбце c должна быть +/- 1 от ее положения в столбце c - 1 . Линия также может идти на два воображаемых ряда над верхней частью доски.
Мы можем использовать динамическое программирование , чтобы найти число способов , чтобы нарисовать линию на первых гр столбцах , которые включают р пешку на этих колоннах, находится на высоту ч на с - м столбца, используя результаты для столбца с - 1 , высотой Н + / - 1 , а количество пешек p - h .
источник
Ява
В настоящее время мой код очень длинный и утомительный, я работаю над тем, чтобы сделать его быстрее. Я использую рекурсивный метод, чтобы найти значения. Он вычисляет первые 5 в течение 2 или 3 секунд, но потом становится намного медленнее. Кроме того, я еще не уверен, верны ли цифры, но первые несколько, похоже, совпадают с комментариями. Любые предложения приветствуются.
Вывод
объяснение
Основная идея - рекурсия. По сути, вы начинаете с пустой доски, доски со всеми нулями. Рекурсивный метод просто проверяет, может ли он положить черную или белую пешку в следующую позицию, если он может поместить только один цвет, он помещает его туда и вызывает сам. Если он может поместить оба цвета, он вызывает себя дважды, по одному с каждым цветом. Каждый раз, когда он вызывает себя, он уменьшает количество оставленных квадратов и соответствующий цвет. Когда он заполнил всю доску, он возвращает текущий счет + 1. Если он обнаруживает, что невозможно поставить черную или белую пешку в следующую позицию, он возвращает 0, что означает, что это мертвый путь.
Код
Попробуйте это здесь (не работает достаточно быстро для Ideone, поэтому последнее значение не печатается, похоже, мое решение не очень хорошее!)
источник
C ++ с потоками, n =
147156Последний результат - запуск того же кода, что и раньше, на более мощной машине. Теперь он запускался на настольном компьютере с четырехъядерным процессором i7 (Core i7-4770), который достиг 120 = n за 120 секунд. Результат:
Это использует алгоритм динамического программирования. Сначала я размышлял о подходах, где результат будет строиться строка за строкой, но я никогда не мог придумать способ расширить решение, не отслеживая тонну состояний.
Ключевые идеи, которые позволили получить достаточно эффективное решение, были следующими:
Если вы посмотрите на одну диагональ действительной конфигурации, она всегда состоит из последовательности черных пешек, за которой следует последовательность белых пешек (где любая последовательность также может быть пустой). Другими словами, каждая диагональ может быть полностью охарактеризована количеством черных пешек.
Следовательно, состояние, отслеживаемое для каждой диагонали, представляет собой количество допустимых конфигураций пешек для каждой комбинации:
При переходе от одной диагонали к следующей возникает другое ограничение для построения правильных решений: позиция, которая отделяет черных пешек от белых пешек, не может увеличиваться. Таким образом, число допустимых конфигураций рассчитывается как сумма действительных конфигураций предыдущей диагонали для позиций, которые равны или больше.
Основной шаг DP тогда очень прост. Каждое значение в диагонали - это просто сумма значений из предыдущей диагонали. Единственной болезненной частью является правильное вычисление индексов и циклов. Поскольку мы работаем с диагоналями, длина увеличивается во время первой половины вычисления и уменьшается во второй половине, что делает вычисление диапазонов цикла более громоздким. Есть также некоторые соображения относительно значений на границе платы, так как они имеют только диагональных соседей с одной стороны при переходе от диагонали к диагонали.
Количество используемой памяти O (n ^ 3). Я храню две копии данных о состоянии и пинг-понг между ними. Я полагаю, что можно было бы оперировать одним экземпляром данных о состоянии. Но вы должны быть очень осторожны, чтобы никакие значения не обновлялись до полного использования старых значений. Кроме того, это не будет работать хорошо для параллельной обработки, которую я представил.
Сложность во время выполнения ... полиномиальная. В алгоритме есть 4 вложенных цикла, поэтому на первый взгляд это будет выглядеть как O (n ^ 4). Но вам, очевидно, нужны bigints при этих размерах, а сами числа также увеличиваются при больших размерах. Число цифр в результате, кажется, увеличивается примерно пропорционально n, что составляет целое число O (n ^ 5). С другой стороны, я обнаружил некоторые улучшения производительности, которые позволяют избежать прохождения всего диапазона всех циклов.
Таким образом, хотя это все еще довольно дорогой алгоритм, он идет намного дальше, чем алгоритмы, перечисляющие решения, которые все являются экспоненциальными.
Некоторые замечания по реализации:
Основной алгоритм кода.
THREADS
управляет количеством используемых потоков, где разумным началом является количество ядер ЦП:Для этого также нужен класс bigint, который я написал для этой цели. Обратите внимание, что это не класс bigint общего назначения. Этого достаточно для поддержки операций, используемых этим конкретным алгоритмом:
источник
фантом
Вот начальный пост, который устанавливает рамки. Я думаю, что процедура является относительно хорошей, но реализация прямо сейчас отстой. Мне нужно, вероятно, попытаться минимизировать количество вычислений, которые я делаю, и вместо этого просто передать больше констант.
стратегия
По сути, каждая белая пешка должна атаковать других белых пешек. Поэтому я начинаю с размещения белой пешки, размещения пешек в каждом месте, где она атакует, и, по существу, заполнения доски всеми местами, которые должна пройти белая пешка. Я останавливаюсь, если уже добавил слишком много белых пешек. Если в конце этого у меня ровно 2n ^ 2 пешек, это решение. Если меньше, добавьте еще одну белую пешку куда-нибудь, заполните все необходимые места и посчитайте снова. Я рекурсивно делю каждый раз, когда обнаруживается заполнение с менее чем 2n ^ 2, и вычисляю количество решений с последней добавленной пешкой и без нее.
Код
Вывод
Только делает это до 5 прямо сейчас, но я думаю, что большая часть проблемы находится в реализации.
Тестовое задание
источник