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);
}