Алгоритм поиска максимальной суммы в последовательности перекрывающихся интервалов
проблема, которую я пытаюсь решить, имеет список интервалов в числовой строке, каждый с заранее определенным счетом. Мне нужно вернуть максимально возможный общий балл.
загвоздка в том, что интервалы перекрываются, а из перекрывающихся интервалов я могу использовать только один. Вот пример.
Intervals - Score
0- 5 - 15
4- 9 - 18
10-15 - 12
8-21 - 19
25-30 - 25
здесь, интервалы 0-5, 4-9 и 8-21 перекрытия.
Интервалы 10-15 и 8-21 также перекрываются.
Максимальная сумма составит 55 (18+12+25).
Это здесь важно отметить, что мы выбираем интервал 4-9 первой партии перекрывающихся интервалов, даже если он не имеет наивысшего балла из трех.
Это потому, что выбор интервала 8-21 бы помешать нам через интервал 10-15 позже, тем самым уменьшая общую сумму (в этом случае общая сумма будет 19+25=44).
Я ищу O(nlogn) или O (n) решение этой проблемы. Я думаю, что динамическое программирование можно использовать, но я могу ошибаться. Может ли кто-нибудь предложить решение/алгоритм(ы), которые могли бы сделать трюк здесь?
Edit: интервалы не находятся в определенном порядке.
6 ответов
это взвешенная вариация интервал планирования; это разрешимо в O(N log N)
с динамическое программирование.
пусть интервал будет g(start, stop, score)
, и пусть они будут отсортированы по stop
. Для простоты предположим, что все stop
уникален.
пусть best[i]
лучший результат, который мы можем получить, когда нам разрешено использовать g[1], ..., g[i]
. Мы не должны использовать их все, конечно, и вообще мы не можем, потому что подмножество интервалов использование должно быть неперекрывающимся.
- явно
best[0] = 0
. То есть, поскольку мы не можем использовать какой-либо интервал, лучший результат, который мы можем получить, - 0. - любой
1 <= k <= N
мы:-
best[k] = max( best[k-1], best[j] + g[k].score )
, где-
j
самый большой индекс такой, чтоg[j].stop < g[k].start
(j
может быть ноль)
-
-
то есть, учитывая, что нам разрешено использовать g[1], ... g[k]
, лучшее, что мы можем сделать, это лучший результат этих два варианта:
- мы не включаем
g[k]
. Таким образом, оценка этой опцииbest[k-1]
.- ... потому что это лучшее, что мы можем сделать с
g[1], ... g[k-1]
- ... потому что это лучшее, что мы можем сделать с
- включить
g[k]
, и слева от него мы делаем все возможное со всеми генами, которые не перекрываются сg[k]
, т. е.g[1], ..., g[j]
, гдеg[j].stop < g[k].start
иj
как можно больше. Таким образом, оценка этой опцииbest[j] + g[k].score
.
(обратите внимание на оптимальная подструктура и перекрывающиеся подзадачи компонентов динамического программирования, воплощенные в приведенном выше уравнении).
общий ответ на вопрос best[N]
, то есть лучший результат, который мы можем получить, когда нам разрешено использовать все гены. Ой, я сказал гены? Я имею в виду интервалы.
это O(N log N)
потому что:
- сортировка всех интервалов принимает
O(N log N)
- найти
j
для каждогоk
isO(log N)
используя бинарные поиск
если несколько генов могут иметь одно и то же stop
значения, затем ничего не изменилось: вам все равно придется искать самый правый j
. Например, Python это легко с bisect_right
. В Java, где стандартный двоичный поиск библиотеки не гарантирует, какой индекс возвращается в случае связей, вы можете (среди многих опций) следовать за ним с линейным поиском (для O(N)
наихудшая производительность) или другая серия бинарных поисков, чтобы найти правильный большинство индексов.
Ой, я опять сказал гены? Я имею в виду интервалы.
вопросы
прежде всего, я думаю, что максимум 59, а не 55. Если вы выберете интервалы [0-5], [8-21] и [25,30], вы получите 15+19+25=59. Для этого можно использовать какое-то динамическое программирование.
сначала вы сортируете все интервалы по их начальной точке, а затем повторяете от конца до начала. Для каждого элемента в списке вы выбираете максимальную сумму от этой точки до последней как max(S[i]+S[j], S[i+1])
, где i-элемент, на котором вы находитесь, j-элемент, который является первой неперекрывающейся записью после вашего элемента (то есть первый элемент, начало которого больше конца текущего элемента). Чтобы ускорить алгоритм, необходимо сохранить максимальную частичную сумму S[j] для каждого элемента.
чтобы уточнить, позвольте мне решить ваш пример в соответствии с этим. Сначала отсортируйте интервалы:
1: 0- 5 - 15
2: 4- 9 - 18
3: 8-21 - 19
4: 10-15 - 12
5: 25-30 - 25
и
S[5] = 25
S[4] = max(12+S[5], 25)=37
S[3] = max(19+S[5], S[4])=max(19+25,37)=44
S[2] = max(18+S[4], S[3])=max(18+37,44)=55
S[1] = max(15+S[3], S[2])=max(15+44, 55)=59
Это адаптация алгоритма в этот пост, но, к сожалению, не имеет хорошего времени работы O(n). Вырожденный список, где каждая запись перекрытие следующего приведет к тому, что он будет O(n^2).
может быть подход как в ответ может использоваться, что O (n) по крайней мере для этой проблемы. Это означало бы повторять один раз через интервалы и отслеживать только те комбинации интервалов, которые все еще могут привести к оптимальному окончательному решению.
звучит как вариант проблемы рюкзака. Вы можете найти вдохновение в поиске этих решений.
сколько интервалов мы говорим о? Если это только около 5 (как в вашем примере), вероятно, более практично просто попробовать каждую комбинацию. Если это больше, будет ли аппроксимация идеального решения? Опять же, решения рюкзака (такие как алгоритм жадного приближения Джорджа Данцига) могут быть хорошим местом для начала.
Я немного подумал об этом и кое-что придумал.
Интервал Деревьев обеспечить эффективный способ поиска всех интервалов, которые перекрывают заданный интервал. Проходя через весь набор интервалов, мы можем найти все перекрывающиеся интервалы для данного. Как только мы их получим, мы сможем найти интервал с самым высоким счетом, сохранить его и двигаться дальше.
построение дерева занимает O (N Log N) время, а поиск занимает O(Log N) время. Потому что мы делаем при поиске всех элементов решение становится O (N Log N).
однако, если мы сталкиваемся с чем-то вроде примера выше, где интервал наивысшего балла в одной группе уменьшает общую сумму, алгоритм терпит неудачу, потому что у нас нет способа узнать, что интервал наивысшего балла не должен использоваться перед рукой. Очевидным способом обойти это было бы вычислить оба (или все) итога, если мы не уверены, но это возвращает нас к потенциально O(N^2) или худшему решению.
Я думаю, что мы можем использовать эту рекурсию...
S[i]
обозначает счет каждого интервалаInterval[i]
обозначает все интервалы
ResMax[i] = max(ResMax[i-1] + S[i] //if i is included
,max(R[i-1],S[i])
)
Я не проверен тщательно, но он должен работать, я верю.