algo

Олимпиадные алгоритмы

Задача: Камни

Условие:

На столе изначально лежат $N$ камней. За ход игрок может взять

Каждый ход можно сделать при наличии достаточного количества камней. Проигрывает тот, кто хода сделать не может.

Разбор решения:

Это один из примеров игровых задач на динамику. Чтобы понять принцип решения, давайте решим сначала более простую задачу, разновидностью которой являются «Камни»:

На столе лежит $N$ палочек. Каждый из двух игроков ходит по очереди. В свой ход игрок берет одну или две палочки. Проигрывает тот, кто взял последнюю палочку. По заданному числу палочек $N$ выведите номер игрока, у которого есть выигрышная стратегия.

Рассмотрим саму концепцию этой игры. Если первым ходит игрок $1$, то задача игрока $1$ – поставить игрока $2$ в такую ситуацию, в которой сам игрок $1$ проиграет. Для большего понимания рассмотрим ситуацию:

На столе лежат четыре палочки. Игрок $1$ точно знает, что если бы на столе лежало три или две палочки, он бы выиграл сам, но сейчас он проиграет. Он может взять только одну или две палочки, и тогда на столе останется три или две соответственно. Но после того, как игрок $1$ сделал свой ход, ход делает игрок $2$, и уже он игрок $1$. В зависимости от ситуации новый игрок $1$ берет одну или две палочки и оставляет игроку $2$ последнюю палочку, так как он на выигрышной позиции.

Составим выражение, значение которого – правда, если игрок $1$ выигрывает, и ложь иначе:

\[M_N = \neg M_{N - 1} \lor \neg M_{N - 2}\]

То есть игрок $1$ выигрывает только в том случае, если он проигрывает при количестве палочек, меньшем на $1$ или $2$, и проигрывает, если при количестве палочек, меньшем на $1$ или $2$, выигрывает.

Поняв принцип решения, вернемся к задаче про камни. Тут формулой в одну строку не обойтись, задача усложнилась и появились условия:

\[\begin{equation} M_N=\begin{cases} \neg M_{N - 1} \lor \neg M_{N - 2}, & \text{$N \mod 3 = 0$}.\\ \neg M_{N - 1} \lor \neg M_{N - 3}, & \text{$N \mod 3 = 1$}.\\ \neg M_{N - 1} \lor \neg M_{N - 2} \lor \neg M_{N - 3}, & \text{$N \mod 3 = 2$}. \end{cases} \end{equation}\]

($mod$ – остаток от деления левого операнда на правый, $\neg$ – логическое не, $\lor$ – логическое или)

Выходит, что заолненная по этой формуле таблица выглядит так:

$n$ 1 2 3 4 5 6
решение true true false true true false

Из формулы следует, что нам нужно три проверки на делимость $N$ на $3$. В таблицу запишем значения для $N = 1$ и $N = 2$.

memory = {1: True, 2: True}

Напоминаю, что функция вернет true, если при заданном количестве палочек выигрывает игрок $1$. Дальше просто программируем выведенную формулу:

    memory = {1: True, 2: True}
    
    def win(n):
        if n in memory:
            return memory[n]
        else:
            if n % 3 == 0:
                    if not win(n - 1) or not win(n - 2):
                        memory[n] = True
                        return memory[n]
            if n % 3 == 1:
                    if not win(n - 1) or not win(n - 3):
                        memory[n] = True
                        return memory[n]
            if n % 3 == 2:
                    if not win(n - 1) or not win(n - 2) or not win(n - 3):
                        memory[n] = True
                        return memory[n]
        return False

Если окажется, что ни одно из условий не будет удовлетворено (у игрока $1$ нет никакой выигрышной стратегии), то функция вернет false (последняя строка).