Задания 19–21 ЕГЭ по информатике на Python: теория игр

опубликовано
читать
≈ 5 мин
автор
среда и версия
Python 3.14.2 в браузере (Pyodide 314.0.3) — там же, где запускаются примеры на странице; вывод совпал с CPython 3.14.7
содержание решать задачи Python для начинающих

В заданиях 19–21 ЕГЭ по информатике два игрока по очереди меняют кучу камней, а спрашивают, при каком начальном размере кучи у кого из них есть выигрышная стратегия. Руками это дерево ходов в три-четыре уровня глубиной для каждого начального значения. На Python его перебирает одна рекурсивная функция в семь строк, и она отвечает на все три вопроса. Код ниже запускается прямо на странице. Пример задачи составлен для этой статьи, на экзамене ходы и граница будут другими, а код — тот же.

ЕГЭ / 01

f(29, 2): при любом ходе Пети выигрывает Ваня

f(29, 2): при любом ходе Пети выигрывает ВаняВ куче 29 камней, ходит Петя. Его три хода: плюс один — 30, плюс четыре — 33, вдвое — 58. На каждый из них у Вани есть выигрывающий ответ: из 30 вдвое — 60, из 33 вдвое — 66, из 58 плюс четыре — 62. Во всех трёх случаях в куче не меньше 60, и последний ход сделал Ваня. Поэтому для ходов Пети функция проверяет все варианты — all, а для хода Вани хватает одного удачного — any.позицияход Пети — allответ Вани — any29+1 → 30+4 → 33×2 → 58×2 → 60×2 → 66+4 → 62не меньше 60не меньше 60не меньше 60куда бы ни пошёл Петя, Ваня выигрывает первым ходом: f(29, 2) — True
Когда ходит соперник нужного игрока, выигрыш должен получиться при любом его ходе — all. Когда ходит сам нужный игрок, ему хватит одного удачного хода — any. Из 29 Ваня выигрывает первым ходом, что бы ни сделал Петя.

Пример задачи

Два игрока, Петя и Ваня, играют с кучей камней. Ходят по очереди, первым ходит Петя. За один ход можно добавить в кучу один камень, добавить четыре камня или увеличить число камней вдвое. Игра заканчивается, когда в куче становится не менее 60 камней; побеждает тот, кто сделал последний ход. В начале в куче S камней, 1 ≤ S ≤ 59.

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

Функция, которая отвечает на все вопросы

def f(s, m):
    if s >= 60:
        return m % 2 == 0
    if m == 0:
        return False
    h = [f(s + 1, m - 1), f(s + 4, m - 1), f(s * 2, m - 1)]
    return any(h) if m % 2 != 0 else all(h)

print(f(30, 1), f(29, 2), f(28, 2))
True True False
  • s — сколько камней в куче. m — каким по счёту ходом должен выиграть нужный игрок: 1 — первый ход Пети, 2 — первый ход Вани, 3 — второй ход Пети, 4 — второй ход Вани.
  • Если в куче уже не меньше 60, игра окончена, и m % 2 == 0 отвечает, сделал ли последний ход нужный игрок.
  • Если ходов не осталось (m == 0), а куча не набралась, нужный игрок не успел выиграть.
  • h — ответы для трёх возможных ходов: +1, +4 и ×2. На экзамене сюда подставь ходы из своего условия.
  • Когда ходит нужный игрок (m нечётное), ему хватит одного удачного хода — any. Когда ходит соперник (m чётное), выигрыш должен получиться при любом его ходе — all.

Проверка на трёх позициях. f(30, 1) — True: Петя удвоит кучу до 60 и выиграет первым ходом. f(29, 2) — True: это схема выше, из 29 любой ход Пети даёт Ване победу. f(28, 2) — False: из 28 Петя может положить один камень, и из 29 Ваня одним ходом не выиграет.

Задания 20 и 21

def f(s, m):
    if s >= 60:
        return m % 2 == 0
    if m == 0:
        return False
    h = [f(s + 1, m - 1), f(s + 4, m - 1), f(s * 2, m - 1)]
    return any(h) if m % 2 != 0 else all(h)

print("20:", [s for s in range(1, 60) if not f(s, 1) and f(s, 3)])
print("21:", [s for s in range(1, 60) if not f(s, 2) and f(s, 4)])
20: [25, 28]
21: [24, 27]
  • Задание 20: «Петя не может выиграть первым ходом» — not f(s, 1), «выигрывает вторым ходом при любой игре Вани» — f(s, 3). Подходят 25 и 28 — это и есть два наименьших значения.
  • Задание 21: «нет стратегии выиграть первым ходом» — not f(s, 2), «выигрывает первым или вторым ходом» — f(s, 4). Минимальное значение — 24.

Задание 19: неудачный ход Пети

В задании 19 Петя играет не лучшим образом: его ход «неудачный», то есть хотя бы один из ходов Пети даёт Ване выигрыш. Значит, перебирать нужно через any на каждом уровне, и для этого задания пишут отдельную функцию:

def f19(s, m):
    if s >= 60:
        return m % 2 == 0
    if m == 0:
        return False
    h = [f19(s + 1, m - 1), f19(s + 4, m - 1), f19(s * 2, m - 1)]
    return any(h)

print("19:", min(s for s in range(1, 60) if f19(s, 2)))
19: 15

При 15 камнях Петя может удвоить кучу до 30, и тогда Ваня тоже удвоит её до 60. Ответы к примеру: 19 — 15, 20 — 25 и 28, 21 — 24.

Частые ошибки

Задание 19 через all. Если взять для него общую функцию, получится другой ответ:

def f(s, m):
    if s >= 60:
        return m % 2 == 0
    if m == 0:
        return False
    h = [f(s + 1, m - 1), f(s + 4, m - 1), f(s * 2, m - 1)]
    return any(h) if m % 2 != 0 else all(h)

print("19 через all:", min(s for s in range(1, 60) if f(s, 2)))
19 через all: 29

29 — это наименьшая куча, из которой Ваня выигрывает при любом ходе Пети, а в задании 19 речь о неудачном ходе, то есть хватает одного.

Граница игры. «Не менее 60» — это s >= 60, а «более 60» — уже s > 60, и ответы сдвигаются все сразу: с s > 60 в этом примере задание 19 дало бы 16, а 21 — 25. Сверяй знак с условием до запуска.

Задание 2 о таблицах истинности разобрано в статье «Задание 2 ЕГЭ на Python», задание 6 с черепахой — в статье «Задание 6 ЕГЭ на Python», а какие ещё задания решают программой — в статье «Python для ЕГЭ по информатике». Свой вариант игры удобно отлаживать в песочнице Python: там есть пошаговое выполнение, и видно, как рекурсия спускается по дереву ходов.

Источники