300. 最长递增子序列

300. 最长递增子序列 - 力扣(LeetCode)

class Solution {
public:
  int lengthOfLIS(vector<int>& nums) {
    // A sequence has a minimum length of 1.
    vector<int> Length(nums.size(), 1);
    for (int i = 1; i < nums.size(); ++i) {
      for (int j = 0; j < i; ++j) {
        // Current selected value nums[i] is larger than the subsequence ending
        // with nums[j], which means that nums[i] can be added to the tail of
        // the subsequence.
        if (nums[j] < nums[i])
          Length[i] = max(Length[i], Length[j] + 1);
      }
    }
    return *max_element(Length.begin(), Length.end());
  }
};

还是要牢记,动态规划总是看以当前下标为结尾的子串,因为这样才容易根据需求构造状态转移方程,比如本题就可以构造为

$$ f(i)=\max_{j\ =\ 0}^{i\ -\ 1}\left\{f(j)\right\}+1 $$

具体看上面注释。

二刷

class Solution {
public:
  int lengthOfLIS(vector<int>& nums) {
    int Result = 1;
    vector<int> DP(nums.size(), 1);
    for (int i = 0; i < nums.size(); ++i) {
      for (int j = i; j >= 0; --j) {
        if (nums[j] < nums[i])
          DP[i] = max(DP[i], DP[j] + 1);
      }
      Result = max(Result, DP[i]);
    }
    return Result;
  }
};

这个和一刷是一样的,属于暴力DP,时间复杂度是\(\mathrm O\left(n^2\right)\),但是思路还是非常清晰的,状态转移方程也很明显。

但是官方题解里这个贪心二分解法好像还真挺符合直觉的,思路可以记一下,然后写的时候可以把二分单独提出来。这个真挺有意思的。