MAXimal | |
добавлено: 23 Aug 2011 12:40 Содержание [скрыть] Поиск подотрезка массива с максимальной/минимальной суммойЗдесь мы рассмотрим задачу о поиске подотрезка массива с максимальной суммой ("maximum subarray problem" на английском), а также некоторые её вариации (в том числе алгоритм решения варианта этой задачи в режиме онлайн — описанный автором алгоритма — KADR (Ярослав Твердохлеб)). Постановка задачиДан массив чисел Например, если бы все числа массива Понятно, что задача о поиске минимального подотрезка — по сути та же самая, достаточно лишь изменить знаки всех чисел на противоположные. Алгоритм 1Здесь мы рассмотрим практически очевидный алгоритм. (Дальше мы рассмотрим другой алгоритм, который чуть сложнее придумать, однако его реализация получается ещё короче.) Описание алгоритмаАлгоритм весьма прост. Введём для удобства обозначение: Будем теперь перебирать индекс Формально это означает, что нам надо для текущего Отсюда мы сразу получаем алгоритм решения: мы просто будем хранить, где в массиве Очевидно, этот алгоритм работает за РеализацияДля реализации нам даже не понадобится явно хранить массив частичных сумм Реализация приводится в 0-индексированных массивах, а не в 1-нумерации, как было описано выше. Приведём сначала решение, которое находит просто численный ответ, не находя индексы искомого отрезка: int ans = a[0], sum = 0, min_sum = 0; for (int r=0; r<n; ++r) { sum += a[r]; ans = max (ans, sum - min_sum); min_sum = min (min_sum, sum); } Теперь приведём полный вариант решения, который параллельно с числовым решением находит границы искомого отрезка: int ans = a[0], ans_l = 0, ans_r = 0, sum = 0, min_sum = 0, min_pos = -1; for (int r=0; r<n; ++r) { sum += a[r]; int cur = sum - min_sum; if (cur > ans) { ans = cur; ans_l = min_pos + 1; ans_r = r; } if (sum < min_sum) { min_sum = sum; min_pos = r; } } Алгоритм 2Здесь мы рассмотрим другой алгоритм. Его чуть сложнее понять, но зато он более элегантен, чем приведённый выше, и реализуется чуть-чуть короче. Этот алгоритм был предложен Джеем Каданом (Jay Kadane) в 1984 г. Описание алгоритмаСам алгоритм выглядит следующим образом. Будем идти по массиву и накапливать в некоторой переменной Докажем этот алгоритм. В самом деле, рассмотрим первый момент времени, когда сумма Однако этого недостаточно для доказательства алгоритма. В алгоритме мы, фактически, ограничиваемся в поиске ответа только такими отрезками, которые начинаются непосредственно после мест, когда случалось Но, в самом деле, рассмотрим произвольный отрезок Так или иначе, но получается, что действительно при поиске ответа можно ограничиться только отрезками, начинающимися сразу после позиций, в которых оказывалось РеализацияКак и в алгоритме 1, приведём сначала упрощённую реализацию, которая ищет только числовой ответ, не находя границ искомого отрезка: int ans = a[0], sum = 0; for (int r=0; r<n; ++r) { sum += a[r]; ans = max (ans, sum); sum = max (sum, 0); } Полный вариант решения, с поддержанием индексов-границ искомого отрезка: int ans = a[0], ans_l = 0, ans_r = 0, sum = 0, minus_pos = -1; for (int r=0; r<n; ++r) { sum += a[r]; if (sum > ans) { ans = sum; ans_l = minus_pos + 1; ans_r = r; } if (sum < 0) { sum = 0; minus_pos = r; } } Смежные задачиПоиск максимального/минимального подотрезка с ограничениямиЕсли в условии задачи на искомый отрезок Двумерный случай задачи: поиск максимальной/минимальной подматрицыОписанная в данной статье задача естественно обобщается на большие размерности. Например, в двумерном случае она превращается в поиск такой подматрицы Из описанного выше решения для одномерного случая легко получить решение за Более быстрые алгоритмы решения этой задачи хотя и известны, однако они не сильно быстрее Этот алгоритм Chan, а также многие другие результаты в данной области на самом деле описывают быстрое умножение матриц (где под умножением матриц подразумевается модифицированное умножение: вместо сложения используется минимум, а вместо умножения — сложение). Дело в том, что задача о поиске подматрицы с наибольшей суммой сводится к задаче о поиске кратчайших путей между всеми парами вершин, а эта задача, в свою очередь — сводится к такому умножению матриц. Поиск подотрезка с максимальной/минимальной средней суммойЭта задача заключается в том, что надо найти такой отрезок Конечно, если на искомый отрезок В таком случае применим стандартный приём при работе с задачами о среднем значении: будем подбирать искомую максимальную среднюю величину двоичным поиском. Для этого нам надо научиться решать такую подзадачу: дано число Чтобы решить эту подзадачу, отнимем Таким образом, мы получили решение за асимпотику Решение задачи в режиме онлайнУсловие задачи таково: дан массив из Алгоритм решения этой задачи достаточно сложен. Автор данного алгоритма — KADR (Ярослав Твердохлеб) — описал данный алгоритм в своём сообщении на форуме.
|