最长上升子序列
最长上升子序列的nlogn的做法非常奇妙,学完在这里做个总结。
关于最长上升子序列,最基础的做法就是n2做法,判断从第一个点的位置到某个点这一段的最长上升子序列的长度就是要枚举出这个点的前面的点中比它小的里最长上升子序列长度最长的那个,在此基础上加1。
下面是重点,nlogn的做法。
思路:
1.需要一个数组,数组在最开始定义为一个空数组。
2.从1到n,判断每个位置:
(1)这个位置的数比这个数组的最后一个数大,则将这个数放入数组的最后一个数的后面。
(2)否则,二分查找,找到数组中第一个比这个数大的数,将它替换。
3.最长上升子序列的长度就是数组的长度。
原理:
数组中存的数就是从一到这个数的中间部分的最长上升子序列的长度为数组中这个数所在位置的下标的数中最小的一个,数组中的最后一个数就是目前最长上升子序列的最后一个数,若正在判断的数比最后一个数大,则说明最长上升子序列的长度增加;而另一种情况则是替换数组中第一个比它大的数,替换的这个位置的下标就是从一到这个数的中间部分的最长上升子序列的长度,那前面的数为什么不替换呢,原因前面已经讲了,就是在这个位置的必须是最小的,那么按这个原理判断到最后,数组长度就是最长上升子序列的长度。
这有一道题目:
洛谷P1439 【模板】最长公共子序列
题目描述
给出 1,2,…,n 的两个排列 P1 和 P2 ,求它们的最长公共子序列。
输入格式
第一行是一个数 n。
接下来两行,每行为 n 个数,为自然数 1,2,…,n 的一个排列。
输出格式
一个数,即最长公共子序列的长度。
输入输出样例
输入 #1
5 3 2 1 4 5 1 2 3 4 5
输出 #1
3
说明/提示
- 对于 50% 的数据, n≤103;
- 对于 100% 的数据,n≤105。
105,非常显然n2做法一定超时,那么它要怎么做呢,可以将最长公共子序列问题转化为最长上升子序列问题(将p2数组中的数变成p1数组中对应的下标,求p2的最长上升子序列),接着用上面的nlogn方法求就可以了。
下面是代码:
#include<iostream> using namespace std; int n; int p1[100010],p2[100010]; int wei[100010]; int cnt=0; int zxl[100010]; int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>p1[i]; wei[p1[i]]=i; } for(int i=1;i<=n;i++){ cin>>p2[i]; p2[i]=wei[p2[i]]; } for(int i=1;i<=n;i++){ if(p2[i]>zxl[cnt]){ zxl[++cnt]=p2[i]; } else{ int l=1; int r=cnt; int mid=(l+r)/2; while(l<r){ if(p2[i]>zxl[mid]){ l=mid+1; } else{ r=mid; } mid=(l+r)/2; } zxl[l]=p2[i]; } } cout<<cnt; return 0; }