Многие люди правильно прокомментировали, что приведенный ниже ответ, сделанный несколько лет назад, который использует динамическое программирование, неправильно кодирует решения, позволяющие элементу массива появляться в «подмножестве» несколько раз. К счастью, все еще есть надежда на подход, основанный на DP.
Пусть dp[i][j][k]
= true, если существует подмножество размера k
первых i
элементов входного массива, суммирующего до j
Наш базовый случай - dp[0][0][0] = true
Теперь либо размер k
подмножества первых i
элементов использует a[i + 1]
, либо нет, что дает повторение
dp[i + 1][j][k] = dp[i][j - a[i + 1]][k - 1] OR dp[i][j][k]
Сложите все вместе:
given A[1...N]
initialize dp[0...N][0...M][0...K] to false
dp[0][0][0] = true
for i = 0 to N - 1:
for j = 0 to M:
for k = 0 to K:
if dp[i][j][k]:
dp[i + 1][j][k] = true
if j >= A[i] and k >= 1 and dp[i][j - A[i + 1]][k - 1]:
dp[i + 1][j][k] = true
max_sum = 0
for j = 0 to M:
if dp[N][j][K]:
max_sum = j
return max_sum
придавая O(NMK)
временную и пространственную сложность.
Возвращаясь назад, мы сделали одно предположение здесь неявно, а именно, что A[1...i]
все неотрицательны. При отрицательных числах инициализация второго измерения 0...M
неверна. Рассмотрим подмножество размера K
, состоящее из подмножества размера K - 1
с суммой, превышающей M
, и еще одного достаточно отрицательного элемента A[]
, так что общая сумма больше не превышает M
. Точно так же наше подмножество размера K - 1
можно суммировать до некоторого крайне отрицательного числа, а затем с достаточно положительным элементом A[]
суммировать до M
. Чтобы наш алгоритм работал в обоих случаях, нам нужно увеличить второе измерение с M
до разницы между суммой всех положительных элементов в A[]
и суммой всех отрицательных элементов (сумма абсолютных значений всех элементов в A[]
).
Что касается того, существует ли решение нединамического программирования, безусловно, существует наивное экспоненциальное решение методом грубой силы и варианты, которые оптимизируют постоянный множитель в экспоненте.
Сверх того? Что ж, ваша проблема тесно связана с суммой подмножества, и литература по широко известным неполным задачам NP довольно обширна. И, как общий принцип, алгоритмы могут быть всех форм и размеров - для меня вполне возможно представить себе, например, рандомизацию, аппроксимацию (просто выберите параметр ошибки достаточно малым!), Простое старое сокращение других полных проблем NP ( преобразуйте вашу проблему в гигантскую логическую схему и запустите решатель SAT). Да это разные алгоритмы. Они быстрее, чем решение для динамического программирования? Наверное, некоторые из них. Являются ли они такими же простыми для понимания или реализации, не говоря уже об обучении, помимо стандартного материала по введению в алгоритмы? Возможно нет.
Это вариант задачи о ранце или подмножестве, где с точки зрения времени (за счет экспоненциального роста требований к пространству по мере увеличения размера ввода) динамическое программирование является наиболее эффективным методом, который ПРАВИЛЬНО решает эту проблему. См. Этот вариант проще решить проблему с суммой подмножества? на вопрос, аналогичный вашему.
Однако, поскольку ваша проблема не совсем такая, я все равно дам объяснение. Пусть dp[i][j]
= true
, если есть подмножество длины i
, которое суммируется до j
и false
, если его нет. Идея состоит в том, что dp[][]
будет кодировать суммы всех возможных подмножеств для каждой возможной длины. Затем мы можем просто найти самый большой j <= M
, такой, что dp[K][j]
будет true
. Наш базовый случай dp[0][0] = true
, потому что мы всегда можем создать подмножество, которое суммируется до 0, выбрав одно из размера 0.
Повторение также довольно простое. Предположим, мы вычислили значения dp[][]
, используя первые n
значений массива. Чтобы найти все возможные подмножества первых n+1
значений массива, мы можем просто взять n+1
_-е значение и добавить его ко всем подмножествам, которые мы видели раньше. Более конкретно, у нас есть следующий код:
initialize dp[0..K][0..M] to false
dp[0][0] = true
for i = 0 to N:
for s = 0 to K - 1:
for j = M to 0:
if dp[s][j] && A[i] + j < M:
dp[s + 1][j + A[i]] = true
for j = M to 0:
if dp[K][j]:
print j
break
person
weeb
schedule
06.08.2013