Теория игр, информатика и питон. Задачи ЕГЭ 19-21

Теория игр, информатика и питон. Задачи ЕГЭ 19-21.

 

Хотите готовиться со мной к ЕГЭ?
Пишите:
ydkras@mail.ru
Немного обо мне.

 

Внимание! Данный материал устарел! Новую версию статьи читайте здесь:
https://ege-informatika-yk.blogspot.com/2024/02/19-21.html

 

 

 

В задачах 19-21 ЕГЭ по информатике требуется проанализировать некую простую игру. Однако хотя игра и проста, её анализ может оказаться достаточно хитрым делом. Поэтому приходит мысль: а нельзя ли, чтобы этот анализ выполнил компьютер? (Спойлер: Можно!)

Немного теории.

Основную идею, лежащую в основе теории игр, подобных описанным в задачах ЕГЭ, можно выразить в двух словах: игроки умные. Это означает, что если у кого-то из игроков в текущей позиции есть возможность сделать выигрышный ход, то он этот ход сделает.

Таким образом, если в текущей позиции у игрока есть хотя бы один ход, ведущий к выигрышу, то это - выигрышная позиция для данного игрока. 

Если же в ответ на любой ход текущего игрока его противник выигрывает, то для текущего игрока такая ситуация - проигрышная.

Теперь более-менее понятно, как должна выглядеть программа, анализирующая ход игры. Прежде всего она должна проверить, не выиграл ли уже кто-то из игроков. Если это так - то дальнейший анализ позиции не требуется. 

Затем нужно просмотреть все возможные ходы текущего игрока. Если них есть хоть один выигрышный для него - в данной позиции он выиграл. Если же при любом его ходе выигрывает его противник - то можно считать, что данный игрок уже проиграл.

Если же неверно ни первое, ни второе - то ситуация неопределенная.(Вообще-то для игр из задач ЕГЭ всегда можно сказать, кто из игроков выиграет, такая неопределенность возникает только из-за ограничения на число ходов.)

Данную процедуру следует рекурсивно применять к позициям, возникающим по ходу игры.

А теперь - практика.

Решим программно задачи 19-21 из 8 варианта с сайта Полякова. Условие выглядит следующим образом:


(№ 3084) (А. Кабанов) Два игрока, Петя и Ваня, играют в следующую игру. Перед игроками лежит куча камней. Игроки ходят по очереди, первый ход делает Петя. За один ход игрок может
  а) добавить в кучу один камень;
  б) увеличить количество камней в куче в два раза;
  в) увеличить количество камней в куче в три раза.
Игра завершается в тот момент, когда количество камней в куче становится не менее 43. Если при этом в куче оказалось не более 72 камней, то победителем считается игрок, сделавший последний ход. В противном случае победителем становится его противник. В начальный момент в куче было S камней, 1≤S≤42.
Ответьте на следующие вопросы:
  Вопрос 1. Найдите минимальное значение S, при котором Ваня выигрывает своим первым ходом при любой игре Пети.
  Вопрос 2. Сколько существует значений S, при которых у Пети есть выигрышная стратегия, причём одновременно выполняются два условия:
− Петя не может выиграть за один ход;
− Петя может выиграть своим вторым ходом независимо от того, как будет ходить Ваня.
  Вопрос 3. Найдите минимальное и максимальное значения S, при которых одновременно выполняются два условия:
– у Вани есть выигрышная стратегия, позволяющая ему выиграть первым или вторым ходом при любой игре Пети;
– у Вани нет стратегии, которая позволит ему гарантированно выиграть первым ходом.
Найденные значения запишите в ответе в порядке возрастания.


Приведем текст рекурсивной функции hod на языке Питон, которая по сути и решает данную задачу.

def hod(number,limit,s):
    player = (number+1)%2 + 1  # номер текущего игрока (1 или 2)
    rival = 3 - player    # Номер его противника (2 или 1)
    #Анализ позиции
    if s >= 43 and s <= 72: return rival  # текущий игрок проиграл
    if s > 72 : return player  # проиграл противник

    if number > limit: return 0  # ситуация неопределенная из-за ограничений на количество ходов
    
    # Делаем ходы и проверяем результаты
    rc1 = hod(number+1,limit,3*s)
    if rc1 == player: return player
    
    rc2 = hod(number+1,limit,2*s)
    if rc2 == player: return player

    rc3 = hod(number+1,limit,s+1)
    if rc3 == player: return player
    
    if rc1 == rival and rc2 == rival and rc3 == rival: return rival
    
    return 0 

 

Функция имеет три параметра: number (номер хода), limit (ограничение на количество ходов) и s (количество камней в куче). При первом обращении следует задавать значение number,  равное 1 (т.е. ходы мы нумеруем с единицы).

Переменная player принимает значение 1 для нечетных номеров ходов (т.е. ходы 1, 3 и т.д. совершает первый игрок) и значение 2 - для четных номеров. Переменная rival, наоборот, равна 2 для нечетных номеров ходов и 1 - для четных. Таким образом, player - это номер игрока, который делает текущий ход, а rival - номер его противника.

Прежде всего функция проверяет, не завершилась ли игра. Если число камней S не менее 43 и не более 72, то это означает, что текущий игрок проиграл: перед этим его противник сделал победный ход. Функция завершается, вернув значение rival. Если же число камней более 72, то соперник текущего игрока только что сделал проигрышный для себя ход, и выиграл текущий игрок. Программа возвращает значение player. 

Далее проверяется ограничение на число ходов. Если это ограничение превышено (number>limit), то дальнейшие ходы не выполняются и программа возвращает значение 0 (т.е. победитель не определён).

Далее осуществляются рекурсивные вызовы функции  hod с параметром number+1 (т.е. номер хода увеличен на единицу), с тем же самым значением limit и числом камней в куче 3S, 2S и S+1 (согласно условиям задачи).

Рекомендую сначала проверять значения, как можно сильнее отличающиеся от исходного (т.е. 3S и 2S) и потом S+1. Это позволяет уменьшить число вершин "дерева игры", которые анализирует программа. С этой же целью программа, обнаружив выигрышный для текущего игрока ход (т.е. результат вызова функции равен player), немедленно возвращает значение player и не выполняет дальнейших проверок ходов.

Если все три результата ходов равны rival, то в данной позиции все ходы текущего игрока приводят к его проигрышу, и программа возвращает значение rival. 

Если же для текущего игрока нет ни одного выигрышного хода, но у него есть ходы, не приводящие к его проигрышу, то ситуация неопределённая, и программа возвращает 0.

Теперь проведем анализ данной игры и получим ответы на заданные в задаче вопросы.

Первый вопрос: каково минимальное значение S, при котором второй игрок выигрывает своим первым ходом? Для ответа на него достаточно вызвать функцию для всех значений S таких, что 1<=S<=42, с ограничением на количество ходов, равным 2 (т.е. игроки делают по одному ходу). Если функция hod вернет значение, равное 2, то при данном начальном значении S второй игрок выигрывает в любом случае.

Второй вопрос: сколько имеется значений S, при которых первый игрок не может выиграть своим первым ходом, но гарантированно выигрывает вторым?

Для этого надо запустить программу два раза, первый раз с ограничением на число ходов 1, а второй раз - с ограничением 3. Если в первом случае программа вернет значение 0 (нет победителя), а второй - 1, то при данном S первый игрок не может гарантированно выиграть своим первым ходом, но непременно выигрывает вторым. Вместо ограничений 1 и 3 можно взять 2 и 4: дополнительный ход второго игрока (если он возможен) ничего не меняет.

Наконец, последний вопрос: при каких значениях S второй игрок не может гарантированно выиграть первым ходом, но обязательно выиграет вторым? Для этого надо опять запустить функцию дважды, с ограничением на число ходов 2 и 4. Если в первом случае функция вернет 0, а во втором - 2, то это искомое значение.   

Вот программа, которая выполняет данные проверки:

 

for s in range(1,43):
    h2 = hod(1,2,s)
    h4 = hod(1,4,s)
    rez = ''
    if h2 == 2: rez = '*** 19 ***'
    if h2 == 0:
        if h4 == 1: rez = '*** 20 ***'
        if h4 == 2: rez = '*** 21 ***'
    print(s,h2, h4, rez)

Результат выполнения программы следующий (из него исключены строки, которые не дают ответов не поставленные вопросы):


7 0 1 *** 20 ***
12 0 2 *** 21 ***
13 0 1 *** 20 ***
14 2 2 *** 19 ***
39 0 2 *** 21 ***
40 0 1 *** 20 ***
41 2 2 *** 19 ***


Следовательно, ответы на вопросы задачи таковы:

Вопрос 1: такие значения - 14 и 41, минимальное - 14.

Вопрос 2: такие значения - 7, 13 и 40, всего их 3.

Вопрос 3: такие значения - 12 и 39. Минимальное - 12, а максимальное - 39.

 

А теперь решим задачи 19-21 из демонстрационного варианта ФИПИ 2022 г.


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

19. Укажите такое значение S, при котором Петя не может выиграть за один ход, но при любом ходе Пети Ваня может выиграть своим первым ходом.

20. Для игры, описанной в задании 19, найдите два таких значения S, при которых у Пети есть выигрышная стратегия, причём одновременно выполняются два условия:
− Петя не может выиграть за один ход;
− Петя может выиграть своим вторым ходом независимо от того, как будет ходить Ваня.
Найденные значения запишите в ответе в порядке возрастания.

21. Для игры, описанной в задании 19, найдите значение S, при котором одновременно выполняются два условия:
− у Вани есть выигрышная стратегия, позволяющая ему выиграть первым или вторым ходом при любой игре Пети;
− у Вани нет стратегии, которая позволит ему гарантированно выиграть первым ходом.
Если найдено несколько значений S, в ответе запишите минимальное из них.


Вопросы по сути те же, что и в задаче с сайта Полякова, однако правила игры другие. 

Перепишем функцию hod для данных правил:

def hod(number,limit,s):
    player = (number+1)%2 + 1  # номер текущего игрока (1 или 2)
    rival = 3 - player    # Номер его противника (2 или 1)
    #Анализ позиции
    if s >= 29: return rival  # текущий игрок проиграл

    if number > limit: return 0  # ситуация неопределенная из-за ограничений на количество ходов
    
    # Делаем ходы и проверяем результаты
    rc1 = hod(number+1,limit,2*s)
    if rc1 == player: return player
    
    rc2 = hod(number+1,limit,s+1)
    if rc2 == player: return player

    if rc1 == rival and rc2 == rival: return rival
    
    return 0 


Основная программа выглядит так же, как и в предыдущем случае. Единственное изменение - вместо range(1,43) в ней записано range(1,29).

Результат выполнения программы следующий (в нём опять-таки опущены несущественные строки):

7 0 1 *** 20 ***
12 0 2 *** 21 ***
13 0 1 *** 20 ***
14 2 2 *** 19 ***

Отсюда видно, что ответ на задачу 19 - 14, на задачу 20 -  7 и 13, а на задачу 21 - 12. Это совпадает с приведенными в варианте ответами.

 

Если Петя глуповат...

При составлении программы мы предполагали, что игроки - умные и ошибочных ходов не делают. Но на сайте "Решу ЕГЭ" очень часто встречаются задачи, где один из игроков дает маху. Вот пример задачи 19 (задача 27416):


Два игрока, Петя и Ваня, играют в следующую игру. Перед игроками лежат две кучи камней. Игроки ходят по очереди, первый ход делает Петя. За один ход игрок может добавить в одну из куч (по своему выбору) один камень или увеличить количество камней в куче в два раза. Например, пусть в одной куче 10 камней, а в другой 5 камней; такую позицию в игре будем обозначать (10, 5). Тогда за один ход можно получить любую из четырёх позиций: (11, 5), (20, 5), (10, 6), (10, 10). Для того чтобы делать ходы, у каждого игрока есть неограниченное количество камней.

Игра завершается в тот момент, когда суммарное количество камней в кучах становится не менее 77. Победителем считается игрок, сделавший последний ход, т. е. первым получивший такую позицию, при которой в кучах будет 77 или больше камней.

В начальный момент в первой куче было семь камней, во второй куче — S камней; 1 ≤ S ≤ 69.

Будем говорить, что игрок имеет выигрышную стратегию, если он может выиграть при любых ходах противника. Описать стратегию игрока — значит, описать, какой ход он должен сделать в любой ситуации, которая ему может встретиться при различной игре противника. В описание выигрышной стратегии не следует включать ходы играющего по этой стратегии игрока, не являющиеся для него безусловно выигрышными, т. е. не являющиеся выигрышными независимо от игры противника.

Известно, что Ваня выиграл своим первым ходом после неудачного первого хода Пети. Укажите минимальное значение S, когда такая ситуация возможна.

 

Сначала напишем нашу функцию hod для данной задачи: она всё равно потребуется для задач 20 и 21. Впрочем, отличий от первого варианта не так много. Во-первых, так как у нас теперь две кучи, у функции есть два параметра s1 и s2: число камней в первой и второй кучах. Условие завершения игры теперь выглядит так: s1+s2>=77. Так как теперь у каждого игрока есть четыре возможных хода, то проверяется не три, а четыре альтернативы.

def hod(number,limit,s1,s2):
    player = (number+1)%2 + 1  # номер текущего игрока (1 или 2)
    rival = 3 - player    # Номер его противника (2 или 1)
    #Анализ позиции
    if s1+s2 >= 77: return rival  # текущий игрок проиграл
   
    if number > limit: return 0  # ситуация неопределенная из-за ограничений на количество ходов
    
    # Делаем ходы и проверяем результаты
    rc1 = hod(number+1,limit,2*s1,s2)
    if rc1 == player: return player
    
    rc2 = hod(number+1,limit,s1,2*s2)
    if rc2 == player: return player

    rc3 = hod(number+1,limit,s1+1,s2)
    if rc3 == player: return player
    
    rc4 = hod(number+1,limit,s1,s2+1)
    if rc4 == player: return player
    
    if rc1 == rival and rc2 == rival and rc3 == rival and rc4 == rival: return rival
    
    return 0

Но как быть с тем, что наша функция предполагает безошибочную игру, а условие задачи подразумевает, что первый игрок сделал ошибочный ход? А очень просто: надо вызвать функцию четыре раза с позициями, которые могут получиться после первого хода Пети. В параметрах функции надо указать, что это - второй ход, и задать ограничение на количество ходов, равное двум. Если хотя бы в одном случае функция даст ответ, что выиграл второй игрок, то это и будет ответом на вопрос задачи.

Таким образом, основная программа выглядит так:


s1=7
for s2 in range(1,70):
    if hod(2,2,s1*2,s2)==2 or hod(2,2,s1,s2*2)==2 or hod(2,2,s1+1,s2)==2 or hod(2,2,s1,s2+1)==2:
        print(s2)
        break


Она дает правильный ответ: 18 камней.

Когда основная функция написана, не представляет сложности решить задачи 20 и 21. Вот их вопросы (правила игры, естественно, те же самые):

20: Найдите два таких значения S, при которых у Пети есть выигрышная стратегия, причём одновременно выполняются два условия:
— Петя не может выиграть за один ход;
— Петя может выиграть своим вторым ходом независимо от того, как будет ходить Ваня.

21: Найдите минимальное значение S, при котором одновременно выполняются два условия:
— у Вани есть выигрышная стратегия, позволяющая ему выиграть первым или вторым ходом при любой игре Пети;
— у Вани нет стратегии, которая позволит ему гарантированно выиграть первым ходом.

Основная программа опять-таки похожа на первый вариант, отличия незначительные:


s1=7
for s2 in range(1,70):
    h2 = hod(1,2,s1,s2)
    h4 = hod(1,4,s1,s2)
    rez = ''
    if h2 == 0:
        if h4 == 1: rez = '*** 20 ***'
        if h4 == 2: rez = '*** 21 ***'
    print(s2,h2, h4, rez)


Результат работы программы (опять-таки опущены несущественные строки):


30 0 2 *** 21 ***
31 0 1 *** 20 ***
33 0 2 *** 21 ***
34 0 1 *** 20 ***


Таким образом, ответ на вопрос задачи 20 - это 31 и 34, а на вопрос задачи 21 - 30 (напомню, что там требуется минимальное значение из двух ответов: 30 и 33).

Итак, мы убедились, что задачи 19-21 ЕГЭ по информатике можно решить программно, и решение - не слишком сложное.

 

(c) Ю.Д.Красильников, 2021 г.

Комментарии

Популярные сообщения из этого блога

Задача 9 (Excel) в 2023 г.

Питон и таблицы истинности

Задача 1 ЕГЭ по информатике - решаем на Питоне