Dynamic Programming

Longest Increasing Subsequence (LIS)

This is a classic problem that we studied in CS341.

Problem Formulation

  • Input: An array of integers
  • Output: A longest increasing subsequence of (or just its length) (does not need to be contiguous)
  • Example: has answer
  • Remark: there are subsequences (including an empty one, which doesn’t count)

My own proof: We define the following subproblem: Find the length of longest increasing subsequence of that includes . We store the value in .

We have a base case initializing .

Let be the second last element part of the LIS. For this to hold, with , then en the LIS is given by + the LIS of that includes

Since we don’t know , we can iterate over all possibilities.

We then come up with the algorithm:

int lengthOfLIS(vector<int>& nums) {
    int ans = 0;
    int dp[nums.size()];
    for (int i=0;i<nums.size();i++) {
        dp[i] = 1;
        for (int j=0;j<i;j++) {
            if (nums[i] > nums[j]) {
                dp[i] = max(dp[j] + 1, dp[i]);
            }
        }
        ans = max(ans, dp[i]);
    }
    return ans;
}

This runs in .

Can we solve it in better than O(n^2)?

Yes, you can do it in :

  • Maintain tails (min possible ending value for each length).
  • For strictly increasing LIS: use lower_bound(tails, x) (first >= x)
  • For non-decreasing: use upper_bound(tails, x) (first > x)
  • If you need “answer at each i”, record pos+1 as you update.

We keep tails sorted/monotonic, and every time, we just binary search and update only 1 of the values.

vector<int> tails;
for (int h : obstacles) {
    int pos = upper_bound(tails.begin(), tails.end(), h) - tails.begin(); // non-decreasing
    if (pos == (int)tails.size()) tails.push_back(h);
    else tails[pos] = h;
    ans.push_back(pos + 1);
}