最长上升子序列问题
两种复杂度 O(n^2) 和 O(nlogn)
思想很简单,以每一个元素为结尾, 内层循环判断大小,
用cnt(i) 表示以下标i的元素为结尾的最大上升值,【0,i】区间内,已经求得最优解,典型的动归思想,利用局部最优求整体最优。
cnt(i) = max( cnt(j)+1,cnt(i) );
先附上较慢的算法O(n^2):
int LIS(vector<int> vec) {
int ans = 1;
for (int i = 1; i < n; i++) {
int t;
for (int j = 0; j < i; j++) {
if (a[j] < a[i]) {
if (vec[j] + 1 > vec[i])
vec[i] = vec[j] + 1;
}
t = vec[i];
}
if (t > ans)
ans = t;
}
return ans;
}
int main()
{
int t;
cin >> t;
while (t--) {
cin >> n;
_for(i, 0, n)
cin >> a[i];
vector<int> vec(n + 1, 1);
int ans = LIS(vec);
cout << ans << endl;
}
// system("pause");
return 0;
}
这个算法的主要在于内层循环浪费时间。
优化在于使用二分,让内层变成logn 的复杂度,可以模拟一个栈。
思想: 用top代表栈顶,也代表此时的最大长度,所以top初始为1。ans[i] = j; 表示长度为i时的结尾为j。
int arr = {5,7,3,6,10,9} 这样一个序列
ans[1] = arr[1] 硬性条件 ans = {5}
arr[2] = 7, 7 > 5 ans[++top] = 7 ans = {5,7}
arr[3] =3, 3 < 7 这时需要调整, 找到ans序列中第一个大于等于arr[3]的值 将其替换 ans = {3,7}
以此类推,遍历完arr. top的值就是最大长度。
可以用STL的lower_bound() 函数寻找其第一个大于等于位置。
int main()
{
int T, p, i, j, k, d;
cin >> T;
while (T--) {
cin >> p >> d;
for (i = 1; i <= p; ++i)
cin >> arr[i];
ans[1] = arr[1];
len = 1;
for (i = 2; i <= p; ++i) {
if (arr[i] > ans[len])
ans[++len] = arr[i];
else {
int pos = lower_bound(ans + 1, ans + len + 1, arr[i]) - ans;
ans[pos] = arr[i];
}
}
printf("%d\n", len);
}
system("pause");
return 0;
}
引用:
LIS的nlogn的优化:
LIS的优化说白了其实是贪心算法,比如说让你求一个最长上升子序列把,一起走一遍。
比如说(4, 2, 3, 1, 2,3,5)这个序列,求他的最长上升子序列,那么来看,如果求最长的上升序列,那么按照贪心,应该最可能的让该序列的元素整体变小,以便可以加入更多的元素。
现在开辟一个新的数组,arr[ 10 ], { .......} --> 这个是他的空间 ,现在开始模拟贪心算法求解最长上升子序列,第一个数是4,先加进去,那么为{ 4 }再来看下一个数2,它比4小,所以如果他与4替换是不是可以让目前子序列(他这一个元素组成的子序列)变得更小,更方便以后元素的加入呢?是的。所以现在为{ 2 } 再来看他的下一个元素3,他要比2大,所以呢加在他的后面,{ 2, 3}
再看下一个元素是1,它比3要小,所以呢为了保证子序列整体尽可能的小(以便可以加入更多的元素),从目前的序列中查找出第一个比他大的数替换掉,那么就变成了{ 1, 3},继续。。 下一个数是2,那么序列变为{ 1,2},再下一个数为3,那么序列为{1,2,3},在下一个数为5,那么序列为{1,2,3,5},完。 目前序列里又4个元素,所以他的最长子序列的个数为4,但是这个序列是一个伪序列,里面的元素,并不是真正的最长上升子序列,而仅仅和最长上升子序列的个数一样。因为查找的时候用的二分查找,所以时间复杂度为o(nlogn)。
————————————————
版权声明:本文为CSDN博主「ltrbless」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ltrbless/article/details/81318935