最长上升子序列nlogn解法

首先回顾一下n^2的做法。 状态转移方程为dp[i] =max(1,dp[j]) (1=<j<=i-1&&a[i]>a[j])

for ( int i = 1 ; i <= n ; i++) cin>>a[i];

	for ( int i = 1 ; i <= n ; i++)
	{
	    dp[i] = 1;
	    for ( int j = 1 ; j < i ; j++)
	    {
		if (a[i]>a[j])
		    dp[i] = max(dp[i],dp[j]+1);
	    }
                ans = max(ans,dp[i]);
	}

然后我们发现,使得dp[i]得到同一个值的dp[j]可能有多个,那么选择哪个呢? 假设 x<y<i,a[x]<a[y],dp[x]==dp[y],那么我们选择x好还是y好呢? 显然是x好。为什么?因为选择x潜力大。因为可能在x,y之间存在一个z,满足a[x]<a[z]<a[y],如果选择a[y],就没有办法选择可能使长度更长的a[z]了。通俗得说。。我们要求的是最长上升子序列。。你一开始就弄那么大。。。后面还上哪上升去啊。。。长度小啊。。。

因此我们得出一个结论,对于dp[t]==k的所有t,要选择a[t]最小的,这样有更大可能得到更长的序列。

我们用g[k]表示所有dp[t]==k的t中,最小的a[t]的值。g[k] = min{A[t]} (dp[t] = k)

我们可以发现关于g的两个性质。

(1) g[k]肯定是单调不增的。因为求最小值嘛,肯

(2)g[1]<g[2]<g[3]....g数组是单调递增的。其实也很好理解,因为是求最长上升子序列,长度为i的序列的最末尾元素肯定要比长度为i+1的末尾的元素小,不然还怎么上升。

g数组初始为无穷大,g[i]表示还没有dp[t]==i的值。

1. 设当前已求出的最长上升子序列的长度为len(初始时为1),每次读入一个新元素x:
  1. 若x>d[len],则直接加入到d的末尾,且len++;(利用性质2)

   否则,在g中二分查找,找到第一个比x小的数g[k],并g[k+1]=x,在这里x<=g[k+1]一定成立(性质1,2)

上面这段引用并没有很懂。。。。。回头再看。

模板如下。

	for ( int i = 1 ; i <= n ; i++) cin>>a[i];
	
	ms(g,0x3f);  
	int ans = 0 ;
	for ( int i = 1 ; i <= n ; i++)
	{
	    int k = lower_bound(g+1,g+n+1,a[i])-g;
//如果是求上升序列即使lower_bound,求不下降就是upper_bound
	    dp[i] = k ;
	    g[k] = a[i];
	    
	    ans = max(ans,dp[i]);
	}