Мое понимание DP - это "составление таблицы".Фактически, первоначальное значение «программирование» в DP - это просто создание таблиц.
Ключ в том, чтобы выяснить что поместить в таблицу , или современные термины: какое состояние отслеживатьили каков ключ / значение вершины в DAG (игнорируйте эти термины, если они кажутся вам странными).
Как насчет выбора dp[i]
таблицы, являющейся самой большой суммой, заканчивающейся индексом i измассив, например, массив [5, 15, -30, 10]
Вторым важным ключом является «оптимальная подструктура», то есть «предположить», что dp[i-1]
уже хранит наибольшую сумму дляподпоследовательности, оканчивающиеся на индекс i-1 , поэтому единственный шаг на i состоит в том, чтобы решить, следует ли включать a[i]
в подпоследовательность или нет
dp[i] = max(dp[i-1], dp[i-1] + a[i])
Первый термин в max
- «не включать [i]», второй термин - «включать [i]».Обратите внимание, если мы не включим a[i]
, самая большая сумма до сих пор остается dp[i-1]
, которая получается из аргумента «оптимальной подструктуры».
Таким образом, вся программа выглядит так (в Python):
a = [5,15,-30,10]
dp = [0]*len(a)
dp[0] = max(0,a[0]) # include a[0] or not
for i in range(1,len(a)):
dp[i] = max(dp[i-1], dp[i-1]+a[i]) # for sub-sequence, choose to add or not
print(dp, max(dp))
Результат: наибольшая сумма подпоследовательности должна быть самым большим элементом в таблице dp
, после i
перебирать массив a
.Но внимательно посмотрите на dp
, он содержит всю информацию.
Поскольку он только один раз проходит через элементы в массиве a
, это алгоритм O (n).
Эта проблема кажется глупой, поскольку, если a[i]
положительно, мы должнывсегда включайте его в подпоследовательность, потому что это только увеличит сумму.Эта интуиция соответствует коду
dp[i] = max(dp[i-1], dp[i-1] + a[i])
Так что макс.Сумма проблемы подпоследовательности проста и не требует DP вообще.Проще говоря,
sum = 0
for v in a:
if v >0
sum += v
Однако как насчет самой большой суммы проблемы "непрерывного подмассива".Все, что нам нужно изменить, - это всего лишь одна строка кода
dp[i] = max(dp[i-1]+a[i], a[i])
Первый термин - это «включить [i] в непрерывный подмассив», а второй - решить начать новуюподмассив, начинающийся с [i].
В этом случае dp[i]
является макс.сумма непрерывного подмассива, оканчивающегося на index-i.
Это, безусловно, лучше, чем наивный подход O (n ^ 2) * O (n), к for j in range(0,i):
внутри i-цикла и sum
все возможные подмассивы.
Одна небольшая оговорка, поскольку задан способ dp[0]
, если все элементы в a
отрицательны, мы не выберем ни одного.Таким образом, для непрерывного подмассива с максимальной суммой мы изменим его на
dp[0] = a[0]