Теория
Динамическое программирование (определение)
Динамическое программирование — это способ решения сложных задач путём сведения их к более простым задачам того же типа (разбиение на подзадачи и повторное использование уже посчитанных результатов).
Базовый пример задания (исполнитель с командами «+1» и «×2»)
Условие. Исполнитель преобразует число на экране. Даны команды:
1) «Прибавить 1» 2) «Умножить на 2».
Программа — это последовательность команд. Сколько существует программ, которые число 1 преобразуют в число 5? Построим дерево вариантов, затем проанализируем его.
Анализ дерева
- Число возможных программ — 4 (узлы-«финиши» выделены зелёным на слайде).
- Число
5можно получить из чисел1,2,3,4— это подсказывает естественную рекурсивную зависимость.
Рекурсивная функция для количества программ
Вопросы к постановке: Сколько параметров у функции? Какие базовые случаи? Какова рекуррентная формула? Ответы с презентации: два параметра (начало и конец), два базовых случая, а рекурсия — сумма путей через «+1» и «×2».
- Параметры:
start(исходное число),finish(целевое число). - База 1: если
start == finish⇒ существует ровно 1 программа (пустая последовательность). - База 2: если
start > finish⇒ программ нет (0). - Рекуррентное соотношение:
F(start,finish) = F(start+1,finish) + F(start*2,finish).
Код
def c_prg ( start,finish ): # 2 параметра: начало подсчета и конечный результат
if start>finish: # базовый случай, начало подсчета больше окончания
return 0
elif start==finish: # базовый случай, начало подсчета=окончанию
return 1
else:
return c_prg (start+1,finish)+ c_prg (start*2,finish) # рекурсивный случай
Практическая пометка: для больших диапазонов добавляйте мемоизацию (@lru_cache) — это ровно «динамическое программирование» через кэш.
Усложнение: обязательный проход через заданное число
Условие : те же команды, но нужно посчитать число программ, которые преобразуют 1 в 6 и обязательно проходят через число 3. На слайде показано дерево и анализ.
Анализ дерева
- Число вариантов — 4.
- Идея разбиения: число программ «от 1 до 3» умножить на число программ «от 3 до целевого». На слайде приведена формула
N<sub>1→6</sub> = N<sub>1→3</sub> · N<sub>3→6</sub>и записьF(1,3)*F(3,6).
Задание 2 (исполнитель «НечетМ»)
Условие: команды: 1) «прибавь 1», 2) «сделай нечётное»: x → 2x+1. Сколько программ переводят 1 → 27, если траектория не содержит число 26? «Траектория» — последовательность всех промежуточных значений.
Идея: модифицировать функцию — добавить третий параметр ignore (список «запрещённых» значений) и сразу возвращать 0, если start попал в этот список.
def c_prg ( start,finish,ignore ):
# 2 параметра: начало и конец; 3-й параметр — массив игнорируемых значений
if start>finish or start in ignore: # базовый случай: вышли за цель или попали в запрещённые
return 0
elif start==finish: # базовый случай: дошли ровно до цели
return 1
else:
return c_prg (start+1,finish,ignore)+ c_prg (start*2+1,finish,ignore) # рекурсия
print ( c_prg (1,27,[26]) ) # решение с избеганием 26
Этот приём легко обобщается: можно передавать множество запретов, финальные «нельзя», ограничения по длине и т.д. (все такие добавки — в духе динамического программирования).
Задание 3 (предпоследняя команда фиксирована)
Условие: исполнитель «Удвоитель» (команды «+1», «×2»). Сколько программ переводят 4 → 24, причём предпоследняя команда — обязательно «+1»? :contentReference[oaicite:17]{index=17}
Анализ: если предпоследняя команда «+1», то финишная конфигурация — либо 22 → +1 → 23 → +1 → 24 (то есть нужен счёт путей до 22), либо 11 → +1 → 12 → ×2 → 24 (нужны пути до 11). Следовательно, ответ равен F(4,22) + F(4,11).
def c_prg ( start,finish ):
if start>finish:
return 0
elif start==finish:
return 1
else:
return c_prg (start+1,finish)+ c_prg (start*2,finish)
print ( c_prg (4,22)+ c_prg (4,11) )
Задание 4 (самая короткая программа при фиксированном числе «+1»)
Условие: «Калькулятор»: 1) «+1», 2) «×2», 3) «×3». Найти длину самой короткой программы, преобразующей 1 → 9217 и содержащей ровно 30 команд «+1». Под длиной понимается общее число команд.
Идея: добавить в функцию два счётчика — числа «+1» и общего количества команд; вернуть общее количество, минимизируя по ветвям; применить принцип динамического программирования (оптимальность подрешений) и кэшировать ответы. :contentReference[oaicite:20]{index=20}
res={}
def f ( a,b,cntplus,cntnode ):
if a>b:
return 10**8
elif cntplus >30:
return 10**8
elif a==b and cntplus ==30:
return cntnode
elif a==b and cntplus !=30:
return 10**8
elif res.get (( a,b ),-1)!=-1:
return res[( a,b )]
else:
s = min(
f(a+1,b,cntplus+1,cntnode+1),
f(a*2,b,cntplus,cntnode+1),
f(a*3,b,cntplus,cntnode+1)
)
res[( a,b )]=s
return s
print(f(1,9217,0,0))
Практическая пометка: вместо словаря res можно использовать @lru_cache, если все параметры — хэшируемые; здесь параметры подходят (целые).
Задания для закрепления
Простые
https://kompege.ru/task?id=413
https://kompege.ru/task?id=951
https://kompege.ru/task?id=21420 Смотреть разбор
https://kompege.ru/task?id=21604 Смотреть разбор
Средние
https://inf-ege.sdamgia.ru/problem?id=52194 Смотреть разбор
https://inf-ege.sdamgia.ru/problem?id=63039 Смотреть разбор
https://education.yandex.ru/ege/variants/31d08c52-c86e-4487-b7e4-6f7435f63344/task/23 Смотреть разбор
23 Крылов С.С. 2026 Вариант 1. Смотреть разбор
Сложные
https://education.yandex.ru/ege/inf/task/77c84b81-ecb6-47c8-b7eb-9ea00ce7dd64
https://education.yandex.ru/ege/inf/task/2cbe56c0-d995-4135-8f25-f3423059b3d3
https://education.yandex.ru/ege/inf/task/0b77515d-c5a0-42a8-9a7a-675e7f477215
https://education.yandex.ru/ege/inf/task/f96caa93-afc7-4ca1-8fff-374f264b4ba8
https://education.yandex.ru/ege/inf/task/3fa24815-142f-4135-ae81-09ed896e77e9
https://education.yandex.ru/ege/inf/task/c7a0d273-5483-4561-9012-e2ebbddfa013
https://education.yandex.ru/ege/inf/task/cc309a3d-ca03-4d47-9c15-41c28673646f
https://education.yandex.ru/ege/inf/task/fff68292-d79a-42b4-b357-0e2355cd43c2
https://education.yandex.ru/ege/inf/task/ba109d7d-5e51-4b2c-a9b1-892eec08a4db
https://education.yandex.ru/ege/inf/task/fc7ab1b0-2ae1-4379-9e30-36ebb7b1683f
https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7619
https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7611
https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=7371
https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=6044
https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=6043
https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=6042
https://kpolyakov.spb.ru/school/ege/gen.php?action=viewTopic&topicId=6038
https://kompege.ru/task?id=18000
https://kompege.ru/task?id=17781
https://kompege.ru/task?id=13302
https://kompege.ru/task?id=9964
https://kompege.ru/task?id=9074
https://kompege.ru/task?id=8377
https://kompege.ru/task?id=7073
https://kompege.ru/task?id=7042
https://kompege.ru/task?id=6274
https://kompege.ru/task?id=5937
https://kompege.ru/task?id=5936
https://kompege.ru/task?id=5837
https://kompege.ru/task?id=5836
https://kompege.ru/task?id=5762
https://kompege.ru/task?id=5236
https://kompege.ru/task?id=4952
https://kompege.ru/task?id=4502
https://kompege.ru/task?id=4500
https://kompege.ru/task?id=4498
https://kompege.ru/task?id=3899
https://kompege.ru/task?id=3780
https://kompege.ru/task?id=3742
https://kompege.ru/task?id=3374
https://kompege.ru/task?id=2341
https://kompege.ru/task?id=2342
https://kompege.ru/task?id=1140
https://kompege.ru/task?id=1139
https://kompege.ru/task?id=1138
https://kompege.ru/task?id=1137
https://kompege.ru/task?id=933


