Страницы

Поиск по вопросам

четверг, 19 декабря 2019 г.

Найти максимальную возрастающую подпоследовательность

#массивы #вектор #cpp


Есть задачка: Дана последовательность целых чисел. Найти ее максимальную возрастающую
подпоследовательность. Нашла решение в интернете, но никак не могу вникнуть(
+1000000 к карме тому, кто сможет мне объяснить принцип действия этой программы
#include 
using namespace std;

/* Finds longest strictly increasing subsequence. O(n log k) algorithm. */
template vector find_lis(vector &a)
{
    vector b, p(a.size());
    int u, v;

    if (a.size() < 1) return b;

    b.push_back(0);

    for (int i = 1; i < (int)a.size(); i++) {
        if (a[b.back()] < a[i]) {
            p[i] = b.back();
            b.push_back(i);
            continue;
        }

        for (u = 0, v = b.size()-1; u < v;) {
            int c = (u + v) / 2;
            if (a[b[c]] < a[i]) u=c+1; else v=c;
        }

        if (a[i] < a[b[u]]) {
            if (u > 0) p[i] = b[u-1];
            b[u] = i;
        }  
    }

    for (u = b.size(), v = b.back(); u--; v = p[v]) b[u] = v;
    return b;
}

/* Example of usage: */
#include 
#include 
int main()
{
    int a[] = { 1, 9, 3, 8, 11, 4, 5, 6, 4, 19, 7, 1, 7 };
    vector seq(a, a+sizeof(a)/sizeof(a[0]));
    vector lis = find_lis(seq);

    for (unsigned i = 0; i < lis.size(); i++)
        printf(i+1 < lis.size() ? "%d " : "%d\n", seq[lis[i]]);
 getch();
    return 0;
}
    


Ответы

Ответ 1



Смотрите, всё не так уж сложно. Код отлично написан с точки зрения мелочей имплементации, но конечно названия переменных и повторное их использование с другим смыслом не добавляет понятности алгоритму. Я переименовал переменные, разделил их где надо и снабдил комментариями. /* Finds longest strictly increasing subsequence. O(n log k) algorithm. */ template vector find_lis(vector &data) { // решаем задачу итеративно. сначала рассматриваем только первый элемент // затем добавляем элементы по одному // (бывший массив p) // вот это интересная штука. тут для каждого индекса i мы рассматриваем // наилучшую из подпоследовательностей, заканчивающихся **на этом элементе**, // и записываем в этот массив индекс предыдущего элемента. // мы пользуемся таким важным свойством: для наилучшей подпоследовательности, // заканчивающейся на i-ом элементе, её кусок без финального элемента обязан быть // наилучшей подпоследовательностью, заканчивающейся на соответствующем // элементе. поэтому для хранения наилучших подпоследовательностей достаточно // лишь одного предыдущего индекса vector previdx(data.size()); // бывший массив b // а здесь мы храним индексы тех элементов, которые **в принципе** могут стать // чьим-то предыдущим элементом в наилучшей последовательности. // список хранится в таком порядке, чтобы соответствующие данные // были отсортированы (чтобы можно было искать двоичным поиском) vector possibleprev; // тривиальный случай if (data.size() < 1) return vector(); possibleprev.push_back(0); // для одного элемента всё просто for (int i = 1; i < (int)data.size(); i++) { // вводим в рассмотрение следующий элемент и обновляем списки T curr = data[i]; // если новый элемент больше всех возможных предыдущих элементов... if (data[possibleprev.back()] < curr) { // ... то всё просто: его предыдущий элемент - наибольший из возможных previdx[i] = possibleprev.back(); // и он сам - тоже возможный наибольший для кого-то после possibleprev.push_back(i); continue; // всё, переходим к следующему элементу } // теперь более интересный случай: мы попали в середину int leftidx, rightidx; // старый знакомый -- поиск половинным делением. // ищем в упорядоченном списке предыдущих элементов, // куда можно вставить новый элемент for (leftidx = 0, rightidx = possibleprev.size()-1; leftidx < rightidx;) { int mididx = (leftidx + rightidx) / 2; if (data[possibleprev[mididx]] < curr) leftidx=mididx+1; else rightidx=mididx; } int foundidx = leftidx; if (curr < data[possibleprev[foundidx]]) { // нашли наш предыдущий индекс! // записываем найденное значение в таблицу previdx: if (foundidx > 0) previdx[i] = possibleprev[foundidx-1]; // и корректируем текущий список possibleprev // ключевой момент - теперь найденный элемент в таблице possibleprev // индекс больше не нужен possibleprev[foundidx] = i; } } // отлично, собираем ответ. это будет последовательность индексов, // "связанная" через previdx vector bestsubseq(possibleprev.size()); // клёвый трюк с индексами, не знал раньше. // обратите внимание, начальное значение seqindex будет bestsubseq.size() - 1 for (int seqindex = bestsubseq.size(), dataindex = possibleprev.back(); seqindex-- != 0; dataindex = previdx[dataindex]) bestsubseq[seqindex] = dataindex; return bestsubseq; } Всё!

Ответ 2



Видимо я тут чего-то важного не понимаю. #include struct range { int start, end; }; // inclusive range struct range max_gtseq (int *a, int n) { struct range result = {0, 0}; int max = 0, start = 0, end = 0; for (int i = 0; i < n - 1; i++) if (a[i] < a[i + 1]) end++; else { if (end - start > max) { max = end - start; result.start = start; result.end = end; } end = start = i + 1; } if ((n - 1) - start > max) { result.start = start; result.end = n - 1; } return result; } int main (int ac, char *av[]) { int x[] = {1, 2, 3, 10, 1, 2, 100, 1, 2, 3, 4, 5, 6, 0}; //{10, 9, 8}; struct range r = max_gtseq(x, sizeof(x) / sizeof(x[0])); printf ("start = %d end = %d\n", r.start, r.end); return puts("End") == EOF; } Неужели такой тривиальный однопроходный алгоритм не работает?

Комментариев нет:

Отправить комментарий