力扣-300-最长递增子序列
作者:互联网
直达链接
想到了连续子数组的最大和
自己想
我本来想倒着推,有点像mari和shiny,但是不对
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int length = nums.size();
if (length < 2) return 1;
vector<int> dp(length);
dp[length - 1] = 1;
nums[length - 2] < nums[length - 1] ? dp[length - 2] = 2 : dp[length - 2] = 1;
for (int i = length-2; i > 0; --i) {
if (nums[i - 1] < nums[i]) {
dp[i - 1] = dp[i] + 1;
}
else {
dp[i - 1] = dp[i];
}
}
return dp[0];
}
};
因为没法很好地解决这两个用例:
// vector<int> nums = { 0,1,0,3,2,3 };
vector<int> nums = { 4,10,4,3,8,9 };
官方题解
1. 动态规划
数组dp[i]
表示nums
中直到i
为止(包括nums[i]
)的子数组中,最长子序列的长度
状态转移方程为:dp[i] = max(dp[j])+1,j<i且nums[j]<nums[i]
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int n = nums.size();
// 由题,n>1,所以省略检查
vector<int> dp(n, 0);
// 遍历dp数组,每一次都将数组元素初始化为1
for (int i = 0; i < n; ++i) {
dp[i] = 1;
for (int j = 0; j < i; ++j) {
// 遍历比i小的dp数组元素,依次比较取大值
// 以0为例:0不会进入循环
// 以1为例:dp[1]=max(dp[1],dp[0])
// 以2为例:dp[2]=max(dp[2],dp[0]),dp[2]=max(dp[2],dp[1])
if (nums[j] < nums[i]) {
// 这里的dp[i]并不总等于1,在前面的二层循环中可能被刷新
// 因为j<i,所以dp[j]的值一定是知道的
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
// 没见过的新函数,用于求STL容器中的最大值
return *max_element(dp.begin(), dp.end());
}
};
2. 贪心+二分查找
(反正我是没想到二分查找在这里怎么用的)
贪心思想是:如果要想让子序列尽量长,就需要让序列元素的“上升”(数值增大)尽量平缓
关键在于一个数组d[i]
,表示长度为i
的子序列的末尾值(即当前序列最大值)的最小值
然后可以用反证法证明数组d[]
是一个单调递增数组
那么意义有二:1. 可以用二分法2. 数组d[]
就是最长递增子序列
以输入序列 [0, 8, 4, 12, 2] 为例:
初始化,len=1,d[1] = nums[0] = 0
第二步插入 8,nums[1] = 8 > d[1] = 0,len++=2,d[2] = 8,d = [0, 8]
第三步插入 4,nums[2] = 4 < d[len] = d[2] = 8,
去找第一个比4小的位置,找到1,即d[1] = 0,
更新d[1+1]=d[2] = 4,d = [0, 4]
len不变仍然为2
第四步插入 12,12>d[2] =4,len++=3,d[3] = 12,d = [0, 4, 12]
第五步插入 2,2<d[3] = 12,
去找第一个比2小的位置,d[1] = 0
更新d[1+1] = d[2] = 4 = 2
d = [0, 2, 12]
遍历整个nums数组,遇到比d数组最后一个(最大一个)元素大的,就追加到d数组后面,并更新最大长度len的值
遇到小的,就(通过二分查找在d数组中)找第一个比它还小的,更新这个元素后一个位置的值
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
int len = 1;// 代表当前最长递增子序列长度
int n = nums.size();
// 省略检查
vector<int> d(n + 1, 0);// 为什么长度是n+1?d[0]没有用,长度为0的子序列没有意义
d[len] = nums[0];// 初始化d[1]
// 怎么不考虑d[n]吗?整个nums都是递增的呢?子序列是否包含自身?
// 主要是nums[n]非法吗?
for (int i = 1; i < n; ++i) {
// 遍历元素
if (nums[i] > d[len]) {
// 这里更新了len自增
d[++len] = nums[i];
}
else {
// 如果小于
int left = 1, right = len, pos = 0;
// 如果找不到说明所有数都比nums[i]大,此时需要更新d[1],所以将pos设为0
// 二分开始,找第一个比nums[i]小的位置
while (left <= right) {
int mid = (left + right) >> 1;
// 返回第一个小于
if (d[mid] < nums[i]) {
pos = mid;
left = mid + 1;
}
else {
right = mid - 1;
}
d[pos + 1] = nums[i];
}
}
return len;
}
}
};
标签:nums,300,递增,len,力扣,int,length,数组,dp 来源: https://www.cnblogs.com/yaocy/p/16554375.html