Справка по алгоритму: как разделить массив на N сегментов с наименьшим возможным наибольшим сегментом (сбалансированное сегментирование)
Я столкнулся с этой проблемой на одном из российских форумов программирования, но не придумал элегантного решения.
:
У вас есть массив с N целых положительных чисел, вам нужно разделить его на M смежных сегментов, так что сумма наибольшего сегмента является наименьшим возможным значением. Под итогом сегмента я подразумеваю сумму всех его целых чисел. Другими словами, Мне нужен хорошо сбалансированный массив сегментация, где вы не хотите, чтобы один сегмент был слишком большим.
пример:
массив: [4, 7, 12, 5, 3, 16]
M = 3, Что означает, что мне нужно разделить мой массив на 3 подрешетки.
решение было бы: [4,7] [12, 5] [3, 16] таким образом, наибольший сегмент равен [3, 16] = 19, и ни один другой вариант сегментации не может создать наибольший сегмент с меньшим всего.
еще пример:
- массив [3, 13, 5, 7, 18, 8, 20, 1]
- M = 4
решение: [3, 13, 5] [7, 18] [8] [20, 1], в "жирный" сегмент [7, 18] = 25 (поправьте меня, если я ошибаюсь, Я сделал этот пример)
У меня такое чувство, что это какая-то классическая задача CS/math, вероятно, с именем какого-то известного человека, связанного с ней, как проблема Дийкстры. - Есть ли известное решение для этого? - Если нет, можете ли вы придумать какое-нибудь другое решение, кроме грубого принуждения, которое, насколько я понимаю сложность времени,экспоненциальный. (N^M, чтобы быть более конкретным).
заранее спасибо, stackoverflowers.
2 ответов
мне нравится подход ILoveCoding. Вот еще один способ, который занимает o (MN^2) Время, что будет быстрее, если N и M малы, но числа в массиве очень большие (в частности, если log(sum) >> MN, что возможно, но, по общему признанию, не звучит очень реалистично). Он использует динамическое программирование.
давайте рассмотрим разбиение только подмножества, состоящего из первых I
легко вычислить f (i, 1) -- это всего лишь сумма первых I элементов:
f(i, 1) = x[1] + ... + x[i]
чтобы вычислить f (i, j) для j >= 2, Обратите внимание, что элемент i должен быть окончательным элемент некоторого сегмента, который начинается с некоторой позиции 1 и в любом оптимальном решении для параметров (i, j) эти J-1 предшествующие сегменты сами должны быть оптимальными для параметров (k-1, j-1). Поэтому, если мы рассмотрим каждую возможную стартовую позицию k для этого конечного сегмента и возьмем лучшее, мы вычислим лучшее J-разбиение первых I элементов:
[EDIT 3/2/2015: нам нужно взять максимум нового сегмент и самый большой оставшийся сегмент, вместо того, чтобы добавлять их!]
f(i, j >= 2) = minimum of (max(f(k-1, j-1), x[k] + ... + x[i])) over all 1 <= k <= i
если мы попробуем K значений в порядке убывания, то мы можем легко построить сумму в постоянное время на K значение, поэтому вычисление одного значения f(i, j) занимает O(N) время. У нас есть MN этих значений для вычисления, поэтому общее время, необходимое O(MN^2).
необходимо еще одно граничное условие, чтобы запретить попытку разбиения на большее количество сегментов, чем есть элементы:
f(i, j > i) = infinity
как только мы вычислили f (N, M), мы могли бы извлечь соответствующий раздел, проследив назад через матрицу DP обычным способом, но в этом случае, вероятно, проще просто построить раздел, используя жадный алгоритм ILoveCoding. В любом случае требуется время O(N).
давайте сделаем двоичный поиск по ответу.
для фиксированного ответа
Xлегко проверить, возможно ли это или нет(мы можем просто использовать жадный алгоритм (всегда беря самый длинный возможный сегмент, чтобы его сумма была<= X) и сравните количество сегментов сM).
общая сложность времени O(N * log(sum of all elements)).
вот какой-то псевдокод
boolean isFeasible(int[] array, long candidate, int m) {
// Here goes the greedy algorithm.
// It finds the minimum number of segments we can get(minSegments).
...
return minSegments <= m;
}
long getMinimumSum(int[] array, int m) {
long low = 0; // too small
long high = sum of elements of the array // definitely big enough
while (high - low > 1) {
long mid = low + (high - low) / 2;
if (isFeasible(array, mid, m))
high = mid;
else
low = mid;
}
return high;
}