- •Глава 5
- •1. Понятие множества. Представление множества для вычислений
- •2. Набор операций для множеств
- •3. Алгоритмы выполнения основных операций над множествами и их эффективность
- •4. Получение полных наборов комбинаторных объектов. Перестановки
- •Void main()
- •Int next_permut(int* p, int n)
- •Void main()
- •5. Подмножества
- •Void main()
- •Int next_subset(int n, int r, int*& b)
- •Void main()
- •6. Разбиения
- •7. Поиск наибольшей монотонно возрастающей подпоследовательности
- •Int subsetnext(int n, int r, int* q)
- •Int subs2(int n, int* X)
- •Int subs3(int n, int* b)
7. Поиск наибольшей монотонно возрастающей подпоследовательности
Рассмотрим вначале более простую формулировку задачи: требуется найти длину наибольшей монотонно возрастающей подпоследовательности (а не саму подпоследовательность).
Алгоритм 1: перебор подпоследовательностей
Пусть задана упорядоченная последовательность чисел: A = <a0, a1, ... , an-1>. Подпоследовательностью из А называют последовательность, которую можно получить из А путем удаления из нее некоторого множества элементов. Задача состоит в том, чтобы найти длину наибольшей монотонно возрастающей подпоследовательности, которая содержится в А.
Самая простая идея состоит в том, чтобы алгоритм решения задачи построить на основе полного перебора всех подпоследовательностей.
Реализация
Любую подпоследовательность из А можно представить, указав множество номеров элементов последовательности А, включаемых в подпоследовательность. Создадим массив q[n] и присвоим его элементам значения 0,1,2, ... , n-1. Будем считать эти числа множеством и будем перебирать все подмножества из него. Для каждого подмножества будем проверять монотонность соответствующей подпоследовательности и фиксировать ее длину.
Для перебора всех подмножеств из множества q можно использовать следующую функцию из syst.h:
Int subsetnext(int n, int r, int* q)
Здесь r – размер выбираемого подмножества. Функция возвращает номер очередного подмножества. Если все подмножества уже исчерпаны, функция возврашает 0.
Оценка эффективности алгоритма
Общее количество подпоследовательностей будет равно числу всех подмножеств из n-элементного множества. Это число равно 2n . Если с - это наибольшее время, которое требуется для проверки одной подпоследовательности, то для времени работы нашего алгоритма будем иметь:
.
Время работы алгоритма в среднем равно:
t = Const2n .
Результаты испытания алгоритма.
Исходная последовательность случайные числа из интервала [0, 999]. Длина исходной последовательности: 150 000. Время выполнения: .
Алгоритм 2: метод рейтинга
Рассмотрим более эффективный алгоритм, который реализует так называемый метод рейтинга. Пусть
A = <a1, a2, ..., an >
исходная последовательность. Свяжем с каждым элементом последовательности ai целое число - рейтинг pi . Всем рейтингам вначале присвоим единичные значения. Рассмотрим следующую процедуру:
for i: =1 to n-1
for j: = i+1 tо n
if aj > ai and pj < pi+1 then pj : = pi+1
Можно убедиться, что после выполнения последней процедуры наибольший из рейтингов будет равен искомой величине длине наибольшей монотонно возрастающей последовательности. Ниже показано состояние массива рейтингов p для каждого шага i процедуры (1). Длина исходной последовательности 10, исходная последовательность A = < 7, 3, 5, 4, 8, 0, 5, 6, 2, 9 > .
| ------------------------------------
| 7 3 5 4 8 0 5 6 2 9
| ------------------------------------
1 | 1 1 1 1 2 1 1 1 1 2
2 | 1 1 2 2 2 1 2 2 1 2
3 | 1 1 2 2 3 1 2 3 1 3
4 | 1 1 2 2 3 1 3 3 1 3
5 | 1 1 2 2 3 1 3 3 1 4
6 | 1 1 2 2 3 1 3 3 2 4
7 | 1 1 2 2 3 1 3 4 2 4
8 | 1 1 2 2 3 1 3 4 2 5
9 | 1 1 2 2 3 1 3 4 2 5
10 | 1 1 2 2 3 1 3 4 2 5
В первой строке указан исходный массив, в левой колонке - номер шага. Красным цветом отмечены элементы массива рейтингов, подвергаемых проверке на текущем шаге. Решением задачи является число L = 5.
Ниже приведена реализация алгоритма решения задачи в виде функции С++.