Задания 19-20-21: Теория игр
- Тема: анализ стратегических игр с кучами камней.
- Ответ: целое число (начальное количество камней, позиция и т.д.).
| Задание | Уровень | Балл | Время | Что нужно |
|---|---|---|---|---|
| 19 | базовый | 1 | 6 мин | Определить, кто выигрывает при оптимальной игре |
| 20 | повышенный | 1 | 8 мин | Найти выигрышную стратегию за 2 хода |
| 21 | высокий | 1 | 11 мин | Построить полное дерево игры и найти оптимальную стратегию |
Общая формулировка
Два игрока (Петя и Ваня) по очереди совершают ходы. Первый ходит Петя. На каждом ходу игрок выполняет одно из разрешённых действий (например, добавить 1 камень, добавить 2 камня, удвоить количество камней). Игра заканчивается, когда выполняется условие завершения (например, количество камней >= 25). Выигрывает тот, кто сделал последний ход (привёл к выполнению условия), или, в некоторых задачах, чей соперник не может ходить.
Задания: N19-20-21.Теория игр | N19-20-21.Разминка
Формулировки из условий
В одной связке 19-21 всегда одни и те же правила игры; отличаются только вопрос и ограничения на длину партии или на ход игрока. Ниже hod (как в шаблоне g(s, hod, end) ниже по файлу) - число уже сделанных полуходов к моменту, когда куча стала терминальной (s выполнило условие конца). Первый полуход зачастую делает Петя.
| Как пишут в КИМ | Смысл для анализа | Как часто кодируют (hod после последнего хода) |
|---|---|---|
| Имеет выигрышную стратегию | Может гарантированно выиграть, если соперник играет как угодно сильно (оптимально) | Рекурсия: на своём полуходе any(...), на чужом all(...) |
| Не может выиграть за один ход | Ни один разрешённый первый ход данного игрока сразу не переводит в терминал с его победой | Для Пети с начала: not g(s, 0, [1]), если победа на 1-м полуходе кодируется hod == 1 |
| Может выиграть за один ход | Существует ход прямо сейчас, после которого игра кончена и победил текущий игрок | Петя с начала: g(s, 0, [1]) (если Петя ходит первым) |
| Выиграть своим вторым ходом (у Пети) | Схема полуходов: Петя, Ваня, Петя - победа на третьем полуходе | g(s, 0, [3]) |
| Выиграть своим первым ходом (у Вани) | Петя, Ваня - победа на втором полуходе | g(s, 0, [2]) |
| Выиграть первым или вторым ходом (у Вани) | Победа не позже чем на 2-м или 4-м полуходе (второй ход Вани - это 4-й полуход) | g(s, 0, [2, 4]) |
| Нет стратегии гарантированно выиграть первым ходом (у Вани) | Нельзя обеспечить победу только сценарием «конец на 2-м полуходе»; иногда нужен ответ Пети и победа на 4-м | not g(s, 0, [2]) при проверке вместе с g(s, 0, [2, 4]) |
| При любом ходе Пети ... Ваня ... | После каждого возможного первого хода Пети выполняется указанное (для проверки со стороны Вани часто «все ветки») | На уровне рекурсии это как раз ветвление и all на ходе Пети, когда оценивают позицию для Вани |
| Независимо от того, как ходит Ваня | После хода Пети любой законный ответ Вани не ломает нужный исход (для победы Пети на 2-м своём ходе и т.п.) | На ходе Вани для цели Пети обычно all(...) |
| Выигрывает после неудачного хода Пети | Петя сходил неоптимально (есть лучший ход, но он выбрал другой), и тогда выполняется условие (часто победа Вани) | Две ветки от s: сравнить исходы s' после +1, *2 и т.д. с ролью игроков |
| Указать минимальное S | Среди подходящих вариантов в диапазоне из условия - наименьшее | break при первом подходящем в прямом range |
| Найти два наименьших S | Два самых маленьких разных подходящих значения, в ответе по возрастанию | Собрать в список, взять sorted(...)[:2] или два первых в отсортированном переборе |
| Победителем считается сделавший последний ход | Терминал наступает в момент хода: кто привёл к s >= N или s <= M (как в условии), тот выиграл |
В базе рекурсии проверяют hod in end, а не «кто ходит следующим» |
| Игра заканчивается, когда ... | Условие конца партии задаётся отдельно: порог по куче, сумме куч и т.д. | if s >= N или if s <= M до разбора ходов |
Если в задаче первым ходит Ваня или счёт полуходов в решении начинают с 1 по-другому, номера hod в колонке таблицы нужно сдвинуть на 1, но порядок чередования Петя - Ваня - Петя обычно сохраняется.
Ключевые понятия
Позиция
Текущее состояние игры (например, количество камней в кучах).
Выигрышная позиция (В)
Позиция, из которой текущий игрок может гарантированно выиграть при любых ходах противника. Достаточно, чтобы хотя бы один возможный ход вёл в проигрышную позицию для соперника.
Проигрышная позиция (П)
Позиция, из которой все возможные ходы ведут в выигрышные позиции для соперника. Текущий игрок проигрывает при оптимальной игре обоих.
Правило определения
- Если из позиции существует ход в проигрышную для соперника позицию -> позиция выигрышная
- Если все ходы ведут в выигрышные для соперника позиции -> позиция проигрышная
Типы заданий
Задание 19: Кто выигрывает?
Вопрос: При каком начальном количестве камней первый (или второй) игрок выигрывает за 1 ход?
Подход: Перебрать начальные позиции и проверить, может ли первый игрок одним ходом завершить игру.
Задание 20: Выигрышная стратегия за 2 хода
Вопрос: При каком начальном количестве камней первый (или второй) игрок выигрывает не более чем за 2 хода?
Подход: Для каждой начальной позиции проверить все комбинации ходов на глубину 2.
Задание 21: Полное дерево игры
Вопрос: При каком начальном количестве камней заданный игрок выигрывает (при оптимальной игре обоих)?
Подход: Построить полное дерево позиций и определить выигрышные/проигрышные позиции снизу вверх.
Наглядные примеры с числами
Пример 1: одна куча
Условия примера:
- одна куча, начально
S = 11 - ходы:
+1илиx2 - конец игры:
S >= 25
Ход Пети 1:
- если Петя делает
x2, получаем22 - тогда Ваня своим первым ходом может сделать
x2и получить44, игра завершена, Ваня победил
Ход Пети 2:
- если Петя делает
+1, получаем12 - из
12Ваня одним ходом не может получить>= 25(13или24)
Этот пример показывает фразу из КИМ: «после неудачного хода Пети Ваня выигрывает своим первым ходом».
Неудачный первый ход Пети здесь - переход в 22.
Пример 2: две кучи
Условия примера:
- кучи
(6, S), возьмёмS = 7, начальная позиция(6, 7) - за ход можно увеличить одну выбранную кучу:
+1илиx3 - конец игры:
a + b >= 65
Четыре первых хода Пети из (6, 7):
(7, 7)(добавил 1 в первую)(6, 8)(добавил 1 во вторую)(18, 7)(утроил первую)(6, 21)(утроил вторую)
Разберём два хода:
- из
(6, 21)Ваня может утроить вторую кучу:(6, 63), сумма69, Ваня выигрывает сразу - из
(7, 7)Ваня одним ходом не завершает игру (максимум(21, 7), сумма28)
Получается, у Пети есть как неудачный, так и более аккуратный первый ход. Это ровно та логика, которую мы кодируем через перебор позиций после первого хода Пети и проверку условия для Вани.
Алгоритм решения (аналитический)
Шаг 1: Выписать условие
- Начальное количество камней (или диапазон)
- Разрешённые ходы (например: +1, +2, x2)
- Условие завершения (например: S >= 25)
- Кто ходит первым (обычно Петя)
Шаг 2: Построить таблицу позиций
| S | +1 | +2 | x2 | Тип позиции |
|---|---|---|---|---|
| 25+ | - | - | - | конец игры |
| 24 | 25 (конец) | 26 (конец) | 48 (конец) | В (любой ход завершает) |
| 23 | 24 (В) | 25 (конец) | 46 (конец) | В |
| ... | ... | ... | ... | ... |
Шаг 3: Определить тип каждой позиции
Двигаемся от конечных позиций к начальным:
- Если хотя бы один ход ведёт в П -> позиция В
- Если все ходы ведут в В -> позиция П
Шаг 4: Ответить на вопрос задачи
- Задание 19: найти S, где первый ход сразу завершает игру
- Задание 20: найти S, где за 2 хода гарантированно выигрыш
- Задание 21: найти S, где при полном дереве игры выигрывает нужный игрок
Алгоритм решения (Python)
1) Одна куча (шаблон)
TARGET = 129
def g1(s, hod, end):
if s >= TARGET:
return hod in end
if hod >= max(end):
return False
moves = [
g1(s + 1, hod + 1, end),
g1(s * 2, hod + 1, end),
]
return any(moves) if ((hod + 1) % 2) == (end[0] % 2) else all(moves)
print([
s for s in range(1, TARGET)
# if g1(s, 0, [2]) # 19: Ваня выиграл 1-м ходом
# if g1(s, 0, [3]) and not g1(s, 0, [1]) # 20: Петя 2-м ходом, но не 1-м
# if g1(s, 0, [2, 4]) and not g1(s, 0, [2])# 21: Ваня 1-м или 2-м, но не гарантированно 1-м
])
2) Две кучи (шаблон)
TARGET = 65
def g2(a, b, hod, end):
if a + b >= TARGET:
return hod in end
if hod >= max(end):
return False
moves = [
g2(a + 1, b, hod + 1, end),
g2(a, b + 1, hod + 1, end),
g2(a * 3, b, hod + 1, end),
g2(a, b * 3, hod + 1, end),
]
return any(moves) if ((hod + 1) % 2) == (end[0] % 2) else all(moves)
print([
s for s in range(1, 59)
# if any(a1 + b1 < TARGET and g2(a1, b1, 1, [2]) for a1, b1 in ((7, s), (6, s + 1), (18, s), (6, 3 * s))) and any(a1 + b1 < TARGET and not g2(a1, b1, 1, [2]) for a1, b1 in ((7, s), (6, s + 1), (18, s), (6, 3 * s)))
# if g2(6, s, 0, [3]) and not g2(6, s, 0, [1])
# if g2(6, s, 0, [2, 4]) and not g2(6, s, 0, [2])
])
3) Задачи с числами на доске (без рекурсии)
def step(board):
b = sorted(board)
return b[:-2] + [b[-2] + b[-1]]
def finish_hod(s, target=100):
board = list(range(1, s + 1))
hod = 0
while max(board) < target:
board = step(board)
hod += 1
return hod
print([
s for s in range(14, 100)
# if finish_hod(s) == 2 # Ваня выигрывает своим первым ходом
# if finish_hod(s) == 3 # Петя выигрывает своим вторым ходом
])
Таблица: формулировка задачи -> код
| Формулировка в условии | Код Python |
|---|---|
| Разрешённые ходы: +1, +2, x2 | moves = [s + 1, s + 2, s * 2] |
Две кучи, конец при a + b >= N |
if a + b >= N: return hod in end |
| Игра заканчивается при S >= N | if s >= N: ... |
| Ход Пети (нечётный) | any(...) (ищет хотя бы 1 выигрышный ход) |
| Ход Вани (чётный) | all(...) (все ходы должны вести к победе Пети) |
| Петя выигрывает за 1 ход | any(m >= N for m in moves) |
| После неудачного 1-го хода Пети (две кучи) | any(g(a1, b1, 1, [2])) and any(not g(a1, b1, 1, [2])) |
| Задачи с числами на доске | while max(board) < target: board = step(board) |
| Выигрывает сделавший последний ход | (step - 1) % 2 == 1 для Пети |
| Найти минимальное S | for s in range(1, 100): if ...: print(s); break |
Типичные ошибки
- Перепутаны OR и AND - на своём ходу
any(), на ходу соперникаall() - Неправильный номер хода - Петя = нечётные (1, 3, 5...), Ваня = чётные (2, 4, 6...)
- Забыли про условие завершения - нужно проверять, что позиция ещё не конечная
- Не учтено, кто сделал последний ход - выигрывает тот, кто привёл к завершению
Полезные советы
- Начинайте с конечных позиций и двигайтесь назад
- Рисуйте дерево игры для маленьких примеров
- Проверяйте на примерах из условия перед сдачей
- Внимательно читайте, кто выигрывает - иногда спрашивают про Ваню, а не Петю
Составлено: Лилия С. Источники: КИМ ЕГЭ 2026, открытый банк ФИПИ, спецификация ЕГЭ по информатике
Удачи на экзамене!