Задача о наибольшей общей возрастающей последовательности — различия между версиями
(→Решение за время O(N2)) |
(→Решение за время O(N4)) |
||
Строка 2: | Строка 2: | ||
==Решение за время O(N<sup>4</sup>)== | ==Решение за время O(N<sup>4</sup>)== | ||
− | Построим следующую динамику: <tex> d[i][j] </tex> - это длина наибольшей возрастающей подпоследовательности массивов <tex> a </tex> и <tex> b </tex>, последний элемент которой <tex> a[i] </tex> и <tex> b[j] (a[i] = b[j]) </tex>. Будем заполнять <tex> d[i][j] </tex> сначала по увеличению <tex> i </tex>, а при равенстве по увеличению <tex> j </tex>. Ответом на задачу будет максимум из всех элементов <tex> d[i][j] </tex> | + | Построим следующую динамику: <tex> d[i][j] </tex> - это длина наибольшей возрастающей подпоследовательности массивов <tex> a </tex> и <tex> b </tex>, последний элемент которой <tex> a[i] </tex> и <tex> b[j] (a[i] = b[j]) </tex>. Будем заполнять <tex> d[i][j] </tex> сначала по увеличению <tex> i </tex>, а при равенстве по увеличению <tex> j </tex>. Ответом на задачу будет максимум из всех элементов <tex> d[i][j] </tex> (где <tex> i = 1...n </tex>, <tex> j = 1...m. </tex>) |
Заполнять <tex> d </tex> будем следующим образом: на очередном шаге сравниваем элементы <tex> a[i] </tex> и <tex> b[j] </tex>. | Заполнять <tex> d </tex> будем следующим образом: на очередном шаге сравниваем элементы <tex> a[i] </tex> и <tex> b[j] </tex>. | ||
Если <tex> a[i] \neq b[j] </tex>, то <tex> d[i][j] = 0 </tex> (так как нет НОВП, оканчивающейся в разных элементах). | Если <tex> a[i] \neq b[j] </tex>, то <tex> d[i][j] = 0 </tex> (так как нет НОВП, оканчивающейся в разных элементах). | ||
− | Если <tex> a[i] = b[j] </tex>, то эти элементы могут быть частью НОВП. Переберём, какие элементы стояли перед ними в массивах <tex> a </tex> и <tex> b </tex>. Заметим, что предыдущие значения <tex> d </tex> уже известны, тогда очередное значение | + | Если <tex> a[i] = b[j] </tex>, то эти элементы могут быть частью НОВП. Переберём, какие элементы стояли перед ними в массивах <tex> a </tex> и <tex> b </tex>. Заметим, что предыдущие значения <tex> d </tex> уже известны, тогда очередное значение <tex> d[i][j] = max(d[k][l] + 1, </tex> для всех <tex> k = 1..i-1 </tex> и <tex> l = 1..j-1), </tex> при условии, что <tex> a[k] = b[l] </tex> |
− | <tex> d[i][j] = max(d[k][l] + 1, </tex> для всех <tex> k = 1..i-1 </tex> и <tex> l = 1..j-1), </tex> при условии, что <tex> a[k] = b[l] </tex> | ||
Для восстановления подпоследовательности можно хранить массив предков <tex> prev[1..n] </tex> массива <tex> a: prev[i] </tex> - индекс предыдущего элемента НОВП, которая оканчивается в <tex> a[i] </tex>. | Для восстановления подпоследовательности можно хранить массив предков <tex> prev[1..n] </tex> массива <tex> a: prev[i] </tex> - индекс предыдущего элемента НОВП, которая оканчивается в <tex> a[i] </tex>. | ||
− | a | + | vector<int> LCIS(vector<int> a, vector<int> b) |
− | d[n][m] //динамика | + | d = int[n][m] // динамика |
− | prev[n] //массив предков | + | prev = int[n] // массив предков |
− | |||
− | |||
'''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] | + | 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[i][j] = d[k][l] + 1 |
− | prev[i] | + | prev[i] = k |
− | //восстановление | + | // восстановление |
− | b_i | + | b_i = 1 // ищем лучшую пару (b_i, b_j) |
− | b_j | + | 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 | + | b_i = i |
− | b_j | + | b_j = j |
− | + | vector<int> answer | |
− | pos | + | pos = b_i // проходим по массиву a, выписывая элементы НОВП |
'''while''' pos != 0 | '''while''' pos != 0 | ||
− | + | answer.push(a[pos]) | |
− | pos | + | pos = prev[pos] |
+ | '''return''' answer | ||
==Решение за время O(N<sup>3</sup>)== | ==Решение за время O(N<sup>3</sup>)== |
Версия 20:28, 5 декабря 2013
Даны два массива:
и . Требуется найти их наибольшую общую возрастающую подпоследовательность (НОВП).Содержание
Решение за время O(N4)
Построим следующую динамику:
- это длина наибольшей возрастающей подпоследовательности массивов и , последний элемент которой и . Будем заполнять сначала по увеличению , а при равенстве по увеличению . Ответом на задачу будет максимум из всех элементов (где , )Заполнять
будем следующим образом: на очередном шаге сравниваем элементы и . Если , то (так как нет НОВП, оканчивающейся в разных элементах). Если , то эти элементы могут быть частью НОВП. Переберём, какие элементы стояли перед ними в массивах и . Заметим, что предыдущие значения уже известны, тогда очередное значение для всех и при условии, чтоДля восстановления подпоследовательности можно хранить массив предков
массива - индекс предыдущего элемента НОВП, которая оканчивается в .vector<int> LCIS(vector<int> a, vector<int> b)
d = int[n][m] // динамика
prev = int[n] // массив предков
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]
max
for i = 1...n
for j = 1...m
if d[b_i][b_j] < d[i][j]
b_i = i
b_j = j
vector<int> answer
pos = b_i // проходим по массиву a, выписывая элементы НОВП
while pos != 0
answer.push(a[pos])
pos = prev[pos]
return answer
Решение за время O(N3)
Улучшим предыдущее решение. Пусть теперь
- динамика, в которой элемент по-прежнему последний представитель НОВП массива , а может не быть быть последним представителем массива . Тогда если , будем "протаскивать" последнее удачное сравнение в динамике: (понять это можно так: , поэтому не последний представитель НОВП из массива , а значит предыдущий элемент НОВП находится в префиксе , но уже посчитан). Если , то одним дополнительным циклом пробежим по и найдём предыдущий элемент НОВП, оканчивающейся в (он меньше ). Из подходящих элементов выберем тот, для которого - максимальна. Правильнее было бы использовать индекс в , то есть без элемента . Но так как последовательность строго возрастает, точно не будет два раза элементом НОВП. Тогда мы, не рассматривая граничные случаи , можем использовать индекс в динамике .для всех
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]
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)
Пусть теперь
- это длина наибольшей общей возрастающей подпоследовательности префиксов и (элементы и могут не входить в НОВП). Вычислять будем по увеличению , а при равенстве - по увеличению . Заметим, что в предыдущем решении при каждом равенстве элементов и приходилось пробегать дополнительным циклом по массиву в поисках элемента, меньшего , для которого на префиксе и была наилучшей. А раз мы считаем сначала по увеличению , то можно считать фиксированным, а - переменным. Тогда давайте в дополнительной переменной хранить лучший элемент и его индекс в массиве , такой, что этот элемент строго меньше и значение динамики для него максимально. Фактически, это решение отличается от предыдущих только более "хитрой" реализацией.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[n][b_j]
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]
См. также
- Задача о наибольшей общей подпоследовательности
- Задача о наибольшей возрастающей подпоследовательности
Источники
- http://codeforces.ru/contest/10/problem/D Codeforces - Задача о наибольшей общей возрастающей подпоследовательности