Longest Rising Subsequence

Posted by TalonFinsky on Wed, 26 Jan 2022 19:26:53 +0100

LIS, LCS algorithm prefix

Longest Ascender Sequence I

Dynamic Programming\(O(n^2)\)

Partition the set by the second to last number.

  • State representation
    • Set Definition
      All ascending subsequences ending in number \(i\)
    • attribute
      Max
  • Set Partition
    • Currently there are only \(i\) numbers, \(f[i] = 1\);
    • There are cases smaller than the current number, \(f[i] = max(f[i], f[j]+1),j\in(0,1,2,3\ldots,i-1)\)

Code

for (int i = 1; i <= n; ++i)
{
    f[i] = 1;
    for (int j = 0; j < i; ++j)
        if (w[j] < w[i]) 
            f[i] = max(f[i], f[j]+1);
}
int res = -1;
for (int i = 1; i <= n; ++i)
    res = max(res, f[i]);

Longest Rising Subsequence II

Greed+Binary\(n\log_{2}^{n}\)

First go up the code for Big Man 233

vector<int>stk;//Analog Stack
    stk.push_back(arr[0]);

    for (int i = 1; i < n; ++i) {
        if (arr[i] > stk.back())
            stk.push_back(arr[i]);
        else
            *lower_bound(stk.begin(), stk.end(), arr[i]) = arr[i];
    }
    cout << stk.size() << endl;

The definition of the stack here is not really the longest ascending subsequence, but the lowest end element in the ascending subsequence of length \(i\). For an increasing subsequence, we want the smaller the end element, the better it will be, so that we can have a wider range of subsequent connectable numbers and a longer length.

Greedy Ideas

  • Nature
    As the length of the increasing subsequence increases, the minimum value of the end element must also increase strictly monotonically.
  • Prove
    Assuming the length of two increasing subsequences is \ (x,y and x < y\), the end elements of both subsequences are the same. So in an increasing subsequence of length \(y\), the end element must be less than the second-to-last element, then the second-to-last element is also less than the end element in the increasing subsequence of length \(x\), which is inconsistent with the nature, because we store the minimum end element in the ascending subsequence of length \(i\).

extend

Kidd's gliding wings

Problem: Find the longest ascending and descending subsequences.

thinking

Solving the longest ascending subsequence is from left to right. Now the longest descending subsequence is required. A longest ascending subsequence can be found in reverse order. The longest ascending subsequence from right to left is the longest descending subsequence from left to right.

Code

w[n + 1] = 0;
for (int i = n; i; -- i) {
    for (int j = n + 1; j > i; -- j) {
        if (w[i] > w[j]) f_dw[i] = max(f_dw[i], f_dw[j] + 1);
            }
    }

Maximum ascending subsequence sum

thinking

Change the set property from the maximum of length to the maximum of the sum of the subsequences.

Code

int res = -1;
for (int i = 1; i <= n; ++i) {
    f[i] = a[i];
    for (int j = 1; j < i; ++j)
        if (a[i] > a[j]) f[i] = max(f[i], f[j] + a[i]);
    res = max(res, f[i]);
}

Mountaineering

Problem: Find the maximum of the sum of the lengths of the longest ascending and descending subsequences on both sides at the inflection point of \(i\).

thinking

Based on Kidd's gliding wing problem, make the longest ascending subsequence once on both sides, and then sum the two at the \(i\) turning point to get the maximum value.

Code

The subsequence solving process is no longer repeated twice here.

for (int i = 1; i <= n; ++i)
        res = max(res, f[i] + f2[i] - 1);
// Subtract 1 because they are duplicated at i.

Chorus Formation

Problem: Find the minimum of the total number minus the sum of the lengths of the longest ascending and descending subsequences on both sides at the \(i) inflection point.

thinking

As with mountaining problems, the longest ascending and descending subsequences of each point are preprocessed, and the sum of the two subsequences is the maximum. The code is no longer repeated.

Intercept Missile

Problem: Find out how many non-ascending subsequences the length of the longest ascending subsequence can be divided into at least one original sequence.

problem analysis

It is no longer repeated for the longest ascending subsequence. The difficulty of this problem is that the original sequence can be completely covered by at least a few subsequences which are not ascending.

Thoughts (based on greed)

Defines an array \(q\), the length of which is the number of ascending subsequences, in which the last number of each ascending subsequence is stored.
Traversing through the original sequence, for each number in the original sequence, we make the following judgment

  1. If none of the numbers in the \(q\) array is larger than that, create a new group to store the number.
  2. If 1 is unsuccessful, find the minimum value greater than or equal to in the \(q\) array and replace it.

Relevant Proof

  1. Array\(q\) is a monotonically increasing sequence
    Consider the array \(q\) as a set, with an initial state of empty set and a numeric size and length of 0. When we traverse each element in the original array \(x\), there must be an unequal relationship \(c>x>=a>=b\), replace \(x\) with \(a\) elements in \(q\), and monotonicity does not change.
    In addition, each step will look for a smaller tail in the set to replace it, and if there is no smaller number, a new maximum element will be added, so the array\(q\) increases with the subscript, and the stored value increases, so it monotonically increases the sequence.
  2. Correctness of Greed
    Adjustment method, specific proof separate solution, will not be expanded here.

Code

int res = 0, cnt = 0;
    for (int i = 0; i < n; i++) {
        f[i] = 1;
        for (int j = 0; j < i; j++)
            if (w[i] <= w[j]) f[i] = max(f[i], f[j] + 1);
        res = max(res, f[i]);
        
        int k = 0;
        while (k < cnt && q[k] < w[i]) k++;
        if (k == cnt) q[cnt++] = w[i];
        else q[k] = w[i];
    }

Missile Defense System

Question: At least how many ascending and descending subsequences are required to fully cover the original array.

Differential Intercept Missile

The subsequences solved by interception missiles are monotonic and not ascending. In this problem, both ascending and non-ascending subsequences are combined to find the minimum of the total number of subsequences.

problem analysis

For each number \(x\) in the original sequence, there are two possibilities that a new subsequence will be generated, either in an ascending subsequence or in a non-ascending subsequence. The generation of this subsequence will affect the two possibilities of the subsequent number, and will continue to affect the answer and result in new results.
Consider whether there is a way to predict the impact of inserting a subsequence of \(x\) on the results without knowing the prophet? The answer is No. Then we have to solve the possibility of all the subsequent results it produces by violence, and at the end we get a minimum of all the possibilities, that is, by using violence search.

algorithm

Now that you're using storm search, a simple analysis of time complexity yields a time complexity of \(O(n2^n)\) that needs to be pruned. This topic can be optimized by referring to the greedy nature of the motif LIS II.

optimization

Two arrays can be used to store the end number of each ascending subsequence and the end value of each non-ascending subsequence, so that we can quickly locate which subsequence we need to insert behind, and the greedy idea that the two arrays must be monotonic, the first one that meets the criteria will exit the loop. Binary optimization can also be used for further optimization.

How to Search

Because the scope of this question n is small, you can use iteration to deepen the search to solve the problem, or you can record a global minimum and keep updating it.

Code

The yls code is pasted here directly.

int n;
int h[N];
int up[N], down[N];

bool dfs(int depth, int u, int su, int sd)
{
    // If the number of ascending sequences + descending sequences > the total number is the upper limit, then backtracking
    if (su + sd > depth) return false;
    if (u == n) return true;

    // Enumerate cases placed in ascending subsequence
    bool flag = false;
    for (int i = 1; i <= su; i ++ )
        if (up[i] < h[u])
        {
            int t = up[i];
            up[i] = h[u];
            if (dfs(depth, u + 1, su, sd)) return true;
            up[i] = t;
            flag = true;
            break;  
        }
    if (!flag) 
    {
        up[su + 1] = h[u];
        if (dfs(depth, u + 1, su + 1, sd)) return true;
    }

    // Enumerate the cases placed in the descending subsequence
    flag = false;
    for (int i = 1; i <= sd; i ++ )
        if (down[i] > h[u])
        {
            int t = down[i];
            down[i] = h[u];
            if (dfs(depth, u + 1, su, sd)) return true;
            down[i] = t;
            flag = true;
            break;
        }
    if (!flag) 
    {
        down[sd + 1] = h[u];
        if (dfs(depth, u + 1, su, sd + 1)) return true;
    }

    return false;
}

int main()
{
    while (cin >> n, n)
    {
        for (int i = 0; i < n; i ++ ) cin >> h[i];

        int depth = 0;
        while (!dfs(depth, 0, 0, 0)) depth ++ ;     // Iteration Deepening Search

        cout << depth << endl;
    }

    return 0;
}

Longest Common Rising Subsequence

State representation f[i][j]

  • aggregate
    Set of all common ascending subsequences that occur in (A[1-i]\) and (B[1-j]\) and end with \(B[j]\)
  • attribute
    max

State calculation

  • \(A[i]\) is not included in a common subsequence

    \[f[i][j] = f[i-1][j] \]

  • \(A[i]\) is contained in a common subsequence
    There must be \(A[i]=B[j]\) first, because \(A[i]\) must be last if it is included.
    Secondly, the set is divided into \(1- (i-1)\) segments according to the second last number, and the solution is obtained. The transfer equation is

\[f[i][j] = Max(f[i-1][k]),k \in {1,2,3,4...j-1} \]

Code

Go directly to yls code.

for (int i = 1; i <= n; i ++ )
    {
        int maxv = 1;
        for (int j = 1; j <= n; j ++ )
        {
            f[i][j] = f[i - 1][j];
            if (a[i] == b[j]) f[i][j] = max(f[i][j], maxv);
            if (a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
        }
    }

    int res = 0;
    for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);