Как решать олимпиадные задачи на языке Python

Раздел: Практические задания -> Подготовка к экзаменам

Основные подходы к решению олимпиадных задач на Python

Олимпиадные задачи по программированию часто требуют нестандартных решений и знания классических алгоритмов. Рассмотрим одну из популярных задач – задачу о рюкзаке 0/1 (Knapsack). Она служит хорошей иллюстрацией применения динамического программирования, рекурсии и оптимизации.

Как найти максимальную стоимость предметов, не превышая вместимость рюкзака?

Эффективное решение строится на динамическом программировании. Создается таблица dp размером (n+1) x (capacity+1), где dp[i][w] – максимальная стоимость, которую можно получить из первых i предметов при весе не более w. Заполнение таблицы происходит по правилу: если вес предмета меньше или равен текущей вместимости, то выбирается максимум между стоимостью без этого предмета и стоимостью с ним.


def knapsack_01(weights, values, capacity):
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        w_i = weights[i-1]
        v_i = values[i-1]
        for w in range(1, capacity + 1):
            if w_i <= w:
                dp[i][w] = max(dp[i-1][w], dp[i-1][w - w_i] + v_i)
            else:
                dp[i][w] = dp[i-1][w]
    return dp[n][capacity]

weights = [2, 3, 4, 5]
values = [3, 4, 5, 6]
capacity = 5
print(knapsack_01(weights, values, capacity))

яндекс задания python (задания от яндекса по python)

7

Python разработчик тестовые задания (примеры тестовых заданий python)

В таблице dp строки соответствуют предметам, столбцы – весу. Значение в правой нижней ячейке и будет ответом. Этот метод гарантирует оптимальность за O(n*capacity).

Как реализовать рекурсивное решение с запоминанием результатов?

Рекурсия с мемоизацией (lru_cache или собственный словарь) позволяет избежать построения полной таблицы, если многие состояния не достигаются. Однако в худшем случае сложность та же, но добавляются накладные расходы на рекурсию.


from functools import lru_cache

def knapsack_rec(weights, values, capacity):
    @lru_cache(maxsize=None)
    def dfs(i, w):
        if i == 0 or w == 0:
            return 0
        if weights[i-1] > w:
            return dfs(i-1, w)
        return max(dfs(i-1, w), dfs(i-1, w - weights[i-1]) + values[i-1])
    return dfs(len(weights), capacity)

print(knapsack_rec([2,3,4,5], [3,4,5,6], 5))

программирование python задание 18 (задание 18 по программированию на python)

7

Важно: при больших capacity может возникнуть переполнение стека рекурсии. В таких случаях лучше использовать итеративное DP.

Типичные ошибки и их устранение

Ошибка 1: Неверный порядок циклов при заполнении таблицы. Внешний цикл должен идти по предметам, внутренний – по весу. Если поменять местами, то предмет может быть использован несколько раз (эффект неограниченного рюкзака).

Ошибка 2: Рекурсия без мемоизации – время экспоненциально растет с числом предметов. Решение: добавить кэш или перейти на DP.

Ошибка 3: Игнорирование ограничений по памяти для двумерного массива при большом capacity. Выход: использовать одномерный массив с обновлением справа налево (для 0/1 рюкзака).

- егэ информатика 19 задание python (19 задание егэ по информатике на python)
- егэ python задачи (задачи егэ по python)
- задачи огэ python (задачи огэ по python)

Дополнительные примеры олимпиадных задач

Задача о рюкзаке с неограниченным количеством предметов

В этом варианте каждый предмет можно брать любое число раз. Решение через одномерное DP, где цикл по весу идет слева направо.

Пример

def unbounded_knapsack(weights, values, capacity):
    dp = [0] * (capacity + 1)
    for w in range(1, capacity + 1):
        for i in range(len(weights)):
            if weights[i] <= w:
                dp[w] = max(dp[w], dp[w - weights[i]] + values[i])
    return dp[capacity]

weights = [2, 3, 4]
values = [3, 4, 5]
capacity = 7
print(unbounded_knapsack(weights, values, capacity))
10

Здесь используется предмет весом 4 (стоимость 5) и предмет весом 3 (стоимость 4) – общая стоимость 9, но можно взять два предмета по 3 (стоимость 8) или один 4 + один 3 = 9, а лучше взять два по 2 (6) + один 3 (4) = 10. Ответ верен.

Задача о разделении множества на два подмножества с равной суммой

Это частный случай рюкзака: нужно определить, можно ли набрать сумму, равную половине общей суммы, используя каждый предмет не более одного раза.

Пример

def can_partition(nums):
    total = sum(nums)
    if total % 2 != 0:
        return False
    target = total // 2
    dp = [False] * (target + 1)
    dp[0] = True
    for num in nums:
        for w in range(target, num - 1, -1):
            if dp[w - num]:
                dp[w] = True
    return dp[target]

print(can_partition([1, 5, 11, 5]))  # [1,5,5] и [11] -> True
print(can_partition([1, 2, 3, 5]))   # False
True
False

Задача о наибольшей возрастающей подпоследовательности (LIS)

Классическая задача на динамическое программирование. Найти длину самой длинной строго возрастающей подпоследовательности.

Пример

def length_of_lis(nums):
    if not nums:
        return 0
    dp = [1] * len(nums)
    for i in range(len(nums)):
        for j in range(i):
            if nums[j] < nums[i]:
                dp[i] = max(dp[i], dp[j] + 1)
    return max(dp)

print(length_of_lis([10, 9, 2, 5, 3, 7, 101, 18]))
4

Подпоследовательность: [2,5,7,101] или [2,3,7,101] – длина 4.

Олимпиадные задачи по Python - comments

En
олимпиады python задачи (python)