最长上升子序列nlog(n)
2015-12-30 12:09
363 查看
nlog(n)的方法从很久之前就看了,愣是没看懂,因为这个条件
现在,我们仔细考虑计算F[t]时的情况。假设有两个元素A[x]和A[y],满足 (F[]保存LIS长度)
(1)x < y < t
(2)A[x] < A[y] < A[t]
(3)F[x] = F[y]
没看懂,当A[x]<A[y]时,怎么会F[x]=F[y]呢?
其实这只是传递一个思想而已,当两者的LIS都一样时取最小值效果更好。只要明白这一点就行了。
再根据那个思想,我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。
注意到D[]的两个特点: (这个特点很重要)
(1) D[k]的值是在整个计算过程中是单调不下降的。
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D
。
利 用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[t]与D[len]。若A [t]
> D[len],则将A[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A [t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[t]。令k = j + 1,则有A [t] <= D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,更新D[k] = A[t]。最后,len即为所要求的最长上 升子序列的长度。
在 上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的 时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法 的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!
例如:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <cstdio>
using namespace std;
#define N 200001
int n,a
,dp
;
int find(int len,int n) //二分,返回值为x,则c[x]>n>c[i-1],返回比n大的最小值
{
int mid,l=1,r=len;
while(l<=r)
{
mid=(l+r)/2;
if(dp[mid]>n)
r=mid-1;
else if(dp[mid]<n)
l=mid+1;
else
return mid;
}
return l;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<=n+1;i++)
dp[i]=N;
dp[1]=a[0];
int max=1;
for(int i=1;i<n;i++)
{
int d=find(max,a[i]); //不知为什么max改为i+1就不能过了,但是在hdu上能过
dp[d]=a[i];
if(d>max)
max=d;
}
cout<<max<<endl;
}
return 0;
}
有个更剪短优化的代码,就是利用STL中的lower_bound
函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置
函数upper_bound()返回的在前闭后开区间查找的关键字的上界,如一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,同样,如果插入元素大于数组中全部元素,返回的是last。(注意:此时数组下标越界!!)
返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置
现在,我们仔细考虑计算F[t]时的情况。假设有两个元素A[x]和A[y],满足 (F[]保存LIS长度)
(1)x < y < t
(2)A[x] < A[y] < A[t]
(3)F[x] = F[y]
没看懂,当A[x]<A[y]时,怎么会F[x]=F[y]呢?
其实这只是传递一个思想而已,当两者的LIS都一样时取最小值效果更好。只要明白这一点就行了。
再根据那个思想,我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。
注意到D[]的两个特点: (这个特点很重要)
(1) D[k]的值是在整个计算过程中是单调不下降的。
(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D
。
利 用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[t]与D[len]。若A [t]
> D[len],则将A[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A [t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[t]。令k = j + 1,则有A [t] <= D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,更新D[k] = A[t]。最后,len即为所要求的最长上 升子序列的长度。
在 上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的 时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法 的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!
例如:
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
a[] | 1 | 7 | 3 | 5 | 9 | 4 | 8 |
c[] | -1 | 1 | 3(7)(4) | 5 | 9(8) |
#include <algorithm>
#include <cstring>
#include <climits>
#include <cstdio>
using namespace std;
#define N 200001
int n,a
,dp
;
int find(int len,int n) //二分,返回值为x,则c[x]>n>c[i-1],返回比n大的最小值
{
int mid,l=1,r=len;
while(l<=r)
{
mid=(l+r)/2;
if(dp[mid]>n)
r=mid-1;
else if(dp[mid]<n)
l=mid+1;
else
return mid;
}
return l;
}
int main()
{
while(~scanf("%d",&n))
{
for(int i=0;i<n;i++)
cin>>a[i];
for(int i=0;i<=n+1;i++)
dp[i]=N;
dp[1]=a[0];
int max=1;
for(int i=1;i<n;i++)
{
int d=find(max,a[i]); //不知为什么max改为i+1就不能过了,但是在hdu上能过
dp[d]=a[i];
if(d>max)
max=d;
}
cout<<max<<endl;
}
return 0;
}
有个更剪短优化的代码,就是利用STL中的lower_bound
函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置
函数upper_bound()返回的在前闭后开区间查找的关键字的上界,如一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,同样,如果插入元素大于数组中全部元素,返回的是last。(注意:此时数组下标越界!!)
返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置
#include<cstdio> #include<algorithm> using namespace std; const int MAX=100100; int num[MAX],top=0; int main() { int n; while(~scanf("%d",&n)) { scanf("%d",&num[0]); top=1; for(int i=1;i!=n;i++) { scanf("%d",&num[i]); int * p=lower_bound(num,num+top,num[i]); //不是很懂 if(p-num==top) ++top; *p=num[i]; } printf("%d\n",top); } }
相关文章推荐
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- LFC1.0.0 版本发布
- Android px、dp、sp之间相互转换
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍
- android中像素单位dp、px、pt、sp的比较
- Android对px和dip进行尺寸转换的方法
- Android根据分辨率进行单位转换-(dp,sp转像素px)
- android 尺寸 dp,sp,px,dip,pt详解
- DP问题各种模型的状态转移方程
- POJ-1695-Magazine Delivery-dp
- nyoj-1216-整理图书-dp
- TYVJ1193 括号序列解题报告
- 对DP的一点感想
- TYVJ上一些DP的解题报告
- soj1005. Roll Playing Games
- 01背包问题
- LeetCode之Maximum Product Subarray
- DP Flow