Задача о наибольшей общей возрастающей последовательности — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Новая страница: «Даны два массива: <tex> a[1..n] </tex> и <tex> b[1..m] </tex>. Требуется найти их ''наибольшую общую возраст...»)
 
Строка 16: Строка 16:
 
  inputData(a, n)  
 
  inputData(a, n)  
 
  inputData(b, m)
 
  inputData(b, m)
  for i = 1...n  
+
  '''for''' i = 1...n  
     for j = 1...m
+
     '''for''' j = 1...m
       if a[i] == b[j]
+
       '''if''' a[i] == b[j]
 
           d[i][j] := 1 //НОВП как минимум 1, состоит из одного элемента a[i] <-> b[j]
 
           d[i][j] := 1 //НОВП как минимум 1, состоит из одного элемента a[i] <-> b[j]
             for k = 1...i-1
+
             '''for''' k = 1...i-1
                 for l = 1...j-1
+
                 '''for''' l = 1...j-1
                   if a[k] == b[l] and a[k] < a[i] and d[i][j] < d[k][l] + 1
+
                   '''if''' a[k] == b[l] '''and''' a[k] < a[i] '''and''' d[i][j] < d[k][l] + 1
 
                       d[i][j] := d[k][l] + 1
 
                       d[i][j] := d[k][l] + 1
 
                       prev[i] := k
 
                       prev[i] := k
 
  //восстановление
 
  //восстановление
  b_i := 1 //ищем лучшую пару (b_i, b_j)
+
  b_i := 1         //ищем лучшую пару (b_i, b_j)
  b_j := 1 //d[b_i][b_j] <tex> \rightarrow </tex> max
+
  b_j := 1         //d[b_i][b_j] <tex> \rightarrow </tex> max
  for i = 1...n
+
  '''for''' i = 1...n
     for j = 1...m  
+
     '''for''' j = 1...m  
       if d[b_i][b_j] < d[i][j]:
+
       '''if''' d[b_i][b_j] < d[i][j]
 
           b_i := i
 
           b_i := i
 
           b_j := j  
 
           b_j := j  
 
  print(d[b_i][b_j]) //размер НОВП
 
  print(d[b_i][b_j]) //размер НОВП
 
  pos := b_i //проходим по массиву a, выписывая элементы НОВП
 
  pos := b_i //проходим по массиву a, выписывая элементы НОВП
  while pos != 0:
+
  '''while''' pos != 0
 
     print(a[pos])
 
     print(a[pos])
 
     pos := prev[pos]       
 
     pos := prev[pos]       
Строка 48: Строка 48:
 
<tex> d[i][j] = max(d[k][j]) + 1 </tex> для всех <tex> k = 1..i-1, a[k] < a[i]). </tex>
 
<tex> d[i][j] = max(d[k][j]) + 1 </tex> для всех <tex> k = 1..i-1, a[k] < a[i]). </tex>
  
  for i = 1...n  
+
  '''for''' i = 1...n  
     for j = 1...m
+
     '''for''' j = 1...m
       if a[i] == b[j]:
+
       '''if''' a[i] == b[j]
 
           d[i][j] := 1 //НОВП как минимум 1, состоит из одного элемента a[i] <-> b[j]
 
           d[i][j] := 1 //НОВП как минимум 1, состоит из одного элемента a[i] <-> b[j]
           for k = 1...i-1
+
           '''for''' k = 1...i-1
             if a[k] < a[i] and d[i][j] < d[k][j] + 1:
+
             '''if''' a[k] < a[i] '''and''' d[i][j] < d[k][j] + 1
 
                 d[i][j] := d[k][j] + 1
 
                 d[i][j] := d[k][j] + 1
 
                 prev[i] := k
 
                 prev[i] := k
       else:
+
       '''else'''
 
           d[i][j] = d[i][j-1]
 
           d[i][j] = d[i][j-1]
 
  //восстановление
 
  //восстановление
  b_i := 1 //ищем лучший элемент d[b_i][m] <tex> \rightarrow </tex> max
+
  b_i := 1         //ищем лучший элемент d[b_i][m] <tex> \rightarrow </tex> max
  for i = 1...n
+
  '''for''' i = 1...n
     if d[b_i][m] < d[i][m]:
+
     '''if''' d[b_i][m] < d[i][m]
 
       b_i := i
 
       b_i := i
 
  print(d[b_i][m]) //размер НОВП
 
  print(d[b_i][m]) //размер НОВП
  pos := b_i //проходим по массиву a, выписывая элементы НОВП
+
  pos := b_i       //проходим по массиву a, выписывая элементы НОВП
  while pos != 0:
+
  '''while''' pos != 0
 
     print(a[pos])
 
     print(a[pos])
 
     pos := prev[pos]  
 
     pos := prev[pos]  
  
 
==Решение за время O(N<sup>2</sup>)==
 
==Решение за время O(N<sup>2</sup>)==
Пусть теперь <tex> d[i][j] </tex> - это наибольшая общая возрастающая подпоследовательность префиксов <tex> a[1..i] </tex> и <tex> b[1..j] </tex>, то есть элементы <tex> a[i] </tex> и <tex> b[j] </tex> могут не входить в НОВП. Вычислять <tex> d[][] </tex> будем по увеличению <tex> i </tex>, а при равенстве - по увеличению <tex> j </tex>. Заметим, что в предыдущем решении при каждом равенстве элементов <tex> a[i] </tex> и <tex> b[j] </tex> приходилось пробегать дополнительным циклом по массиву <tex> a </tex> в поисках элемента, меньшего <tex> a[i] </tex>, для которого <tex> d[k][j] </tex> на префиксе <tex> a[1..k] </tex> и <tex> b[1..j] </tex> была наилучшей. А раз мы считаем <tex> d[][] </tex> сначала по увеличению <tex> i </tex> , то <tex> a[i] </tex> можно считать фиксированным, а <tex> b[j] </tex> - переменным. Тогда давайте в дополнительной переменной хранить лучший элемент и его индекс массива <tex> b[] </tex>, такой, что этот элемент строго меньше <tex> a[i] </tex> и значение динамики для него максимально. Фактически, это решение отличается от предыдущих только более "хитрой" реализацией.
+
Пусть теперь <tex> d[i][j] </tex> - это наибольшая общая возрастающая подпоследовательность префиксов <tex> a[1..i] </tex> и <tex> b[1..j] </tex>, то есть элементы <tex> a[i] </tex> и <tex> b[j] </tex> могут не входить в НОВП. Вычислять <tex> d </tex> будем по увеличению <tex> i </tex>, а при равенстве - по увеличению <tex> j </tex>. Заметим, что в предыдущем решении при каждом равенстве элементов <tex> a[i] </tex> и <tex> b[j] </tex> приходилось пробегать дополнительным циклом по массиву <tex> a </tex> в поисках элемента, меньшего <tex> a[i] </tex>, для которого <tex> d[k][j] </tex> на префиксе <tex> a[1..k] </tex> и <tex> b[1..j] </tex> была наилучшей. А раз мы считаем <tex> d </tex> сначала по увеличению <tex> i </tex> , то <tex> a[i] </tex> можно считать фиксированным, а <tex> b[j] </tex> - переменным. Тогда давайте в дополнительной переменной хранить лучший элемент и его индекс массива <tex> b </tex>, такой, что этот элемент строго меньше <tex> a[i] </tex> и значение динамики для него максимально. Фактически, это решение отличается от предыдущих только более "хитрой" реализацией.
  
 
  d[n][m]
 
  d[n][m]
 
  prev[m]
 
  prev[m]
  for i = 1...n
+
  '''for''' i = 1...n
 
     best_ind := 0 //позиция "лучшего" элемента в массиве b
 
     best_ind := 0 //позиция "лучшего" элемента в массиве b
 
     best := 0    //значение динамики для "лучшего" элемента
 
     best := 0    //значение динамики для "лучшего" элемента
     for j = 1...m
+
     '''for''' j = 1...m
 
         d[i][j] := d[i-1][j]                          //НОВП на префиксе a[1..i-1] и b[1..j], то есть без элемента a[i]
 
         d[i][j] := d[i-1][j]                          //НОВП на префиксе a[1..i-1] и b[1..j], то есть без элемента a[i]
         if a[i] == b[j] and d[i-1][j] < best + 1:      //можем использовать a[i]-тый элемент для увеличения НОВП
+
         '''if''' a[i] == b[j] '''and''' d[i-1][j] < best + 1       //можем использовать a[i]-тый элемент для увеличения НОВП
 
             d[i][j] := best + 1                         
 
             d[i][j] := best + 1                         
 
             p[j] := best_ind                             
 
             p[j] := best_ind                             
         if a[i] > b[j] and d[i-1][j] > best)  //в момент следующего равенства a[i] = b[j'] нам не придётся бежать циклом в поисках элемента k:
+
         '''if''' a[i] > b[j] '''and''' d[i-1][j] > best)  //в момент следующего равенства a[i] = b[j'] нам не придётся бежать циклом в поисках элемента k:
 
                 best := d[i-1][j]              //b[k] < b[j'] = a[i]. Мы считаем элемент a[i] фиксированным и сравниваем кандидатов с ним
 
                 best := d[i-1][j]              //b[k] < b[j'] = a[i]. Мы считаем элемент a[i] фиксированным и сравниваем кандидатов с ним
 
                 best_ind := j
 
                 best_ind := j
 
  //восстановление (по массиву b)
 
  //восстановление (по массиву b)
 
  b_j := 1 //ищем лучший элемент d[b_i][m] <tex> \rightarrow </tex> max
 
  b_j := 1 //ищем лучший элемент d[b_i][m] <tex> \rightarrow </tex> max
  for k = 1...m
+
  '''for''' k = 1...m
     if d[n][b_j] < d[n][j]:
+
     '''if''' d[n][b_j] < d[n][j]
 
       b_j := j
 
       b_j := j
 
  print(d[n][b_j]) //размер НОВП
 
  print(d[n][b_j]) //размер НОВП
 
  pos := b_j //проходим по массиву b, выписывая элементы НОВП
 
  pos := b_j //проходим по массиву b, выписывая элементы НОВП
  while pos != 0:
+
  '''while''' pos != 0
 
     print(b[pos])
 
     print(b[pos])
 
     pos := prev[pos]
 
     pos := prev[pos]

Версия 21:39, 25 ноября 2013

Даны два массива: [math] a[1..n] [/math] и [math] b[1..m] [/math]. Требуется найти их наибольшую общую возрастающую подпоследовательность (НОВП).

Решение за время O(N4)

Построим следующую динамику: [math] d[i][j] [/math] - это длина наибольшей возрастающей подпоследовательность массивов [math] a [/math] и [math] b [/math], последний элемент которой [math] a[i] [/math] и [math] b[j] (a[i] = b[j]) [/math]. Будем заполнять [math] d[i][j] [/math] сначала по увеличению [math] i [/math], а при равенстве по увеличению [math] j [/math]. Ответом на задачу будет максимум из всех элементов [math] d[i][j] [/math], где [math] i = 1...n [/math], [math] j = 1...m. [/math]

Заполнять [math] d [/math] будем следующим образом: на очередном шаге сравниваем элементы [math] a[i] [/math] и [math] b[j] [/math]. Если [math] a[i] \neq b[j] [/math], то [math] d[i][j] = 0 [/math] (так как нет НОВП, оканчивающейся в разных элементах). Если [math] a[i] = b[j] [/math], то эти элементы могут быть частью НОВП. Переберём, какие элементы стояли перед ними в массивах [math] a [/math] и [math] b [/math]. Заметим, что предыдущие значения [math] d[][] [/math] уже известны, тогда очередное значение [math] d[i][j] = max(d[k][l] + 1, [/math] для всех [math] k = 1..i-1 [/math] и [math] l = 1..j-1), [/math] при условии, что [math] a[k] = b[l] [/math]

Для восстановления подпоследовательности можно хранить массив предков [math] prev[1..n] [/math] массива [math] a: prev[i] [/math] - индекс предыдущего элемента НОВП, которая оканчивается в [math] a[i] [/math].

a[n], b[m] 
d[n][m] //динамика
prev[n] //массив предков
inputData(a, n) 
inputData(b, m)
for i = 1...n 
   for j = 1...m
      if a[i] == b[j]
         d[i][j] := 1 //НОВП как минимум 1, состоит из одного элемента a[i] <-> b[j]
            for k = 1...i-1
               for l = 1...j-1
                  if a[k] == b[l] and a[k] < a[i] and d[i][j] < d[k][l] + 1
                     d[i][j] := d[k][l] + 1
                     prev[i] := k
//восстановление
b_i := 1          //ищем лучшую пару (b_i, b_j)
b_j := 1          //d[b_i][b_j] [math] \rightarrow [/math] max
for i = 1...n
   for j = 1...m 
      if d[b_i][b_j] < d[i][j]
         b_i := i
         b_j := j 
print(d[b_i][b_j]) //размер НОВП
pos := b_i //проходим по массиву a, выписывая элементы НОВП
while pos != 0
   print(a[pos])
   pos := prev[pos]      


Решение за время O(N3)

Улучшим предыдущее решение. Пусть теперь [math] d[i][j] [/math] - динамика, в которой элемент [math] a[i] [/math] по-прежнему последний представитель НОВП массива [math] a [/math], а [math] b[j] [/math] может не быть быть последним представителем массива [math] b [/math]. Тогда если [math] a[i] \neq b[j] [/math], будем "протаскивать" последнее удачное сравнение в динамике: [math] d[i][j] = d[i][j-1] [/math] (понять это можно так: [math] a[i] \neq b[j] [/math] , поэтому [math] b[j] [/math] не последний представитель НОВП из массива [math] b [/math], а значит предыдущий элемент НОВП находится в префиксе [math] b[1..j-1] [/math], но [math] d[i][j-1] [/math] уже посчитан). Если [math] a[i] = b[j] [/math], то одним дополнительным циклом пробежим по [math] a[] [/math] и найдём предыдущий элемент НОВП, оканчивающейся в [math] a[i] [/math] (он меньше [math] a[i] [/math]). Из подходящих элементов выберем тот, для которого [math] d[k][j] [/math] - максимальна. Правильнее было бы использовать индекс [math] j-1 [/math] в [math] d[k][j-1] [/math], то есть без элемента [math] b[j] [/math]. Но так как последовательность строго возрастает, [math] b[j] [/math] точно не будет два раза элементом НОВП. Тогда мы, не рассматривая граничные случаи [math] d[i][0] [/math], можем использовать индекс [math] j [/math] в динамике [math] d[k][j] [/math].

[math] d[i][j] = max(d[k][j]) + 1 [/math] для всех [math] k = 1..i-1, a[k] \lt a[i]). [/math]

for i = 1...n 
   for j = 1...m
      if a[i] == b[j]
         d[i][j] := 1 //НОВП как минимум 1, состоит из одного элемента a[i] <-> b[j]
         for k = 1...i-1
            if a[k] < a[i] and d[i][j] < d[k][j] + 1
               d[i][j] := d[k][j] + 1
               prev[i] := k
      else
         d[i][j] = d[i][j-1]
//восстановление
b_i := 1         //ищем лучший элемент d[b_i][m] [math] \rightarrow [/math] max
for i = 1...n
   if d[b_i][m] < d[i][m]
      b_i := i
print(d[b_i][m]) //размер НОВП
pos := b_i       //проходим по массиву a, выписывая элементы НОВП
while pos != 0
   print(a[pos])
   pos := prev[pos] 

Решение за время O(N2)

Пусть теперь [math] d[i][j] [/math] - это наибольшая общая возрастающая подпоследовательность префиксов [math] a[1..i] [/math] и [math] b[1..j] [/math], то есть элементы [math] a[i] [/math] и [math] b[j] [/math] могут не входить в НОВП. Вычислять [math] d [/math] будем по увеличению [math] i [/math], а при равенстве - по увеличению [math] j [/math]. Заметим, что в предыдущем решении при каждом равенстве элементов [math] a[i] [/math] и [math] b[j] [/math] приходилось пробегать дополнительным циклом по массиву [math] a [/math] в поисках элемента, меньшего [math] a[i] [/math], для которого [math] d[k][j] [/math] на префиксе [math] a[1..k] [/math] и [math] b[1..j] [/math] была наилучшей. А раз мы считаем [math] d [/math] сначала по увеличению [math] i [/math] , то [math] a[i] [/math] можно считать фиксированным, а [math] b[j] [/math] - переменным. Тогда давайте в дополнительной переменной хранить лучший элемент и его индекс массива [math] b [/math], такой, что этот элемент строго меньше [math] a[i] [/math] и значение динамики для него максимально. Фактически, это решение отличается от предыдущих только более "хитрой" реализацией.

d[n][m]
prev[m]
for i = 1...n
   best_ind := 0 //позиция "лучшего" элемента в массиве b
   best := 0     //значение динамики для "лучшего" элемента
   for j = 1...m	
       d[i][j] := d[i-1][j]                           //НОВП на префиксе a[1..i-1] и b[1..j], то есть без элемента a[i]
       if a[i] == b[j] and d[i-1][j] < best + 1       //можем использовать a[i]-тый элемент для увеличения НОВП
           d[i][j] := best + 1                         
           p[j] := best_ind                            
       if a[i] > b[j] and d[i-1][j] > best)  //в момент следующего равенства a[i] = b[j'] нам не придётся бежать циклом в поисках элемента k:
               best := d[i-1][j]              //b[k] < b[j'] = a[i]. Мы считаем элемент a[i] фиксированным и сравниваем кандидатов с ним
               best_ind := j
//восстановление (по массиву b)
b_j := 1 //ищем лучший элемент d[b_i][m] [math] \rightarrow [/math] max
for k = 1...m
   if d[n][b_j] < d[n][j]
      b_j := j
print(d[n][b_j]) //размер НОВП
pos := b_j //проходим по массиву b, выписывая элементы НОВП
while pos != 0
   print(b[pos])
   pos := prev[pos]