Разбор задачи: Лопание шариков

Предложенный нами код:

 def maxCoins(nums):
        memo, nums = {}, [1] + [num for num in nums if num] + [1]
        def dfs(l, r):
            if r - l == 1: return 0
            if (l, r) not in memo: memo[(l, r)] = max(nums[l] * nums[i] * nums[r] + dfs(l, i) + dfs(i, r) for i in range(l + 1, r))
            return memo[(l, r)]
        return dfs(0, len(nums) - 1)

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


1. Подготовка входных данных

nums = [1] + [num for num in nums if num] + [1]

Эта строка делает следующее:

  • Добавляет фиктивные единицы (1) в начало и конец массива nums. Эти фиктивные шарики обеспечивают корректные вычисления для крайних шариков, так как на границах массива нас просят считать, что значение шарика равно 1.
  • Исключает нулевые значения из массива, так как шарики с числом 0 не влияют на результат.

Пример:
Вход: nums = [3, 1, 5, 8]
После обработки: nums = [1, 3, 1, 5, 8, 1].


2. Рекурсивная функция dfs

Функция dfs(l, r) вычисляет максимальное количество монет, которые можно собрать, лопая шарики на подотрезке nums[l:r].
Идея: на каждом шаге выбираем шарик i для последнего лопания на отрезке l до r.

if r - l == 1: 
    return 0
  • Если отрезок слишком мал (между l и r нет шариков, т.е. r - l == 1), возвращаем 0, так как лопать нечего.

3. Мемоизация

if (l, r) not in memo:
    memo[(l, r)] = max(nums[l] * nums[i] * nums[r] + dfs(l, i) + dfs(i, r) for i in range(l + 1, r))
  • Проверяется, вычислялся ли уже результат для отрезка (l, r). Если нет, рекурсивно вычисляем результат.
  • Для каждого индекса i на отрезке [l+1, r-1] рассматриваем случай, когда шарик i лопается последним.
    • При этом:
      • Учитывается количество монет за лопание nums[l] * nums[i] * nums[r].
      • Рекурсивно вычисляются монеты для двух оставшихся подотрезков: [l, i] и [i, r].

4. Возврат результата

return memo[(l, r)]
  • После вычисления результат сохраняется в словарь memo для предотвращения повторных вычислений.

5. Инициализация вызова

return dfs(0, len(nums) - 1)
  • Вызывается функция dfs для всего массива (от 0 до len(nums) - 1), чтобы найти оптимальное решение для всех шариков.

Пример работы алгоритма

Рассмотрим входные данные nums = [3, 1, 5, 8].
После обработки: nums = [1, 3, 1, 5, 8, 1].


  1. Начальный вызов

    dfs(0, 5)
    
    • Рассматриваем весь массив [1, 3, 1, 5, 8, 1].
    • Выбираем шарик i для лопания последним из индексов 1 до 4.
  2. Вычисления для подотрезков Для каждого выбранного i:
    • Лопаем шарик i.
    • Вызываем dfs для левого и правого подотрезков.
  3. Пример разбиения
    • Для i = 3 (шарик с числом 5):
      • Лопаем 5, получаем nums[2] * nums[3] * nums[4] = 1 * 5 * 8 = 40.
      • Вычисляем dfs(0, 3) для левого подотрезка [1, 3, 1, 5].
      • Вычисляем dfs(3, 5) для правого подотрезка [5, 8, 1].
  4. Использование мемоизации
    • Если подотрезок уже был обработан, результат берется из memo, а не вычисляется заново.

 



Вы должны Войти или Зарегистрироваться чтобы оставлять комментарии