[algorithm] dynamic programming - longest non descending subsequence (LIS)

Longest Increasing Sequence: in a number sequence, find the longest subsequence (can be discontinuous), so that such subsequence is not decreasing (that is, non decreasing).

For example, sequence a = {1, 2, 3, -1, -2, 7, 9}, its longest non descending subsequence is {1, 2, 3, 7, 9} with length of 5, {1, 2, 3} and {- 2, 7, 9} are also non descending but not the longest.

Dynamic programming solution: dp[i] represents the length of the longest non descending subsequence ending with a[i]. So there are two possibilities for a[i]:

  1. There is a number a[j] (i.e. J < I) before a[i], which makes a[j] < = a[i]. That means that a[i] can form a new undiminished sequence after a[j]. At this time, whether dp[i] is to be updated depends on whether dp[j] + 1 is greater than dp[i]. (dp[j] + 1 indicates the length of LIS ending with a[j] plus a[i])
  2. All elements before a[i] are larger than it, so a[i] can only form a LIS by itself, so dp[i] = 1.

Therefore, the length of the longest non descending subsequence ending with a[i] is the larger of the above two results. State transfer mode:
dp[i]=max(1, dp[j]+1)(j=1,2,⋯ ,i−1 and a[j]<a[i]) dp[i] = max \left ( 1, \ dp[j] + 1 \right ) \left ( j = 1,2,\cdots,i-1\ and\ a[j]< a[i] \right ) dp[i]=max(1, dp[j]+1)(j=1,2,⋯,i−1 and a[j]<a[i])

Write the code:

#include <iostream>
#include <vector>
using namespace std;
const int N = 110;
int main() {
    int n, a[N], dp[N], ans = -1;
    cin >> n;
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    for (int i = 0; i < n; i++) {
        dp[i] = 1;	// The initial value is 1, i.e. take yourself as a sequence
        for (int j = 0; j < i; j++) {
            if (a[i] >= a[j] && dp[j] + 1 > dp[i]) {
                dp[i] = dp[j] + 1;
            }
        }
        ans = max(ans, dp[i]);
    }
    cout << ans;
    return 0;
}

Input:

8   
1 2 3 -9 3 9 0 11
1 2 3 3 9 11

Output:

6	// 1 2 3 3 9 11

The main problem here is to get the length of LIS, but it's really sad to be a good programmer if he doesn't output this sequence.

Solution: because there may be more than one sequence meeting the longest, declare vector < int > pre [n] to save all qualified sequences with precursor pointer in the way of adjacency table. Vector < int > dest stores the index of the end node of LIS.

After that, just use DFS to traverse the pre array:

void dfs(int u) {
    path.push_back(u);
    if (pre[u].size() == 0) {
        for (int i = path.size() - 1; i >= 0; i--) {
            printf("%d", a[path[i]]);
            if (i != 0) printf(" ");
        }
        printf("\n");
        path.pop_back();    // 1
        return;             // 2 you can use these two sentences without adding them. In order to show that they have come to an end, return
    }
    for (auto it : pre[u]) dfs(it);
    path.pop_back();  // All the children of u have access to it. Remove U from the path array
}

Complete implementation:

#include <iostream>
#include <vector>
using namespace std;
const int N = 110;
int n, a[N], dp[N], maxlen = -1, ansIndex = 0;
vector<int> pre[N], path, ans;
void dfs(int u) {
    path.push_back(u);
    if (pre[u].size() == 0) {
        for (int i = path.size() - 1; i >= 0; i--) {
            printf("%d", a[path[i]]);
            if (i != 0) printf(" ");
        }
        printf("\n");
        path.pop_back();    // 1
        return;             // 2 you can use these two sentences without adding them. In order to show that they have come to an end, return
    }
    for (auto it : pre[u]) dfs(it);
    path.pop_back();  // All the children of u have access to it. Remove U from the path array
}
int main() {
    cin >> n;
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    for (int i = 0; i < n; i++) {
        dp[i] = 1;
        for (int j = 0; j < i; j++) {
            if (a[i] >= a[j]) {
                if (dp[j] + 1 > dp[i]) {  // If it is longer than the original LIS, the precursor of i will be cleared and j will be added
                    dp[i] = dp[j] + 1;
                    pre[i].clear();
                    pre[i].push_back(j);
                } else if (dp[j] + 1 == dp[i]) {  // If it's the same length, then j can be added as an option
                    pre[i].push_back(j);
                }
            }
        }
        if (dp[i] > maxlen) {
            maxlen = dp[i];
            ans.clear();
            ans.push_back(i);
        } else if (dp[i] == maxlen) {
            ans.push_back(i);
        }
    }
    for (auto it : ans) dfs(it);
    return 0;
}

Input:

8   
1 2 3 -9 3 9 0 11

Output: (there is only one substring satisfying LIS)

1 2 3 3 9 11
Published 25 original articles, praised 0, visited 48
Private letter follow

Tags: Programming

Posted on Fri, 14 Feb 2020 02:25:57 -0500 by justinwhite93