300. 最长递增子序列
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)\),但是思路还是非常清晰的,状态转移方程也很明显。
但是官方题解里这个贪心二分解法好像还真挺符合直觉的,思路可以记一下,然后写的时候可以把二分单独提出来。这个真挺有意思的。