线性DP:N(log N)的复杂度解决最长不下降子序列问题

    当解决最长不下降子序列时,常见的做法是从最后一位向前枚举,同时从该位向后枚举,如果当前位a[ i ]比a[ j ]大且f[ i ]<f[ j ]+1,那么f[ i ]=f[ j ]+1,最后再从前向后扫一遍查询最大值。显然,这种做法并不优秀【什么鬼】,如果序列元素的个数大一点就很容易超时,这里介绍一种N(log N)级别的算法,可以解决当元素个数达到1e5的问题。

    思路:我们用dp[ i ]表示处理到当前位置时,之前的整数能构成的长度为i的序列中,第i位上的最小值【即系列中最后一位最小值】,对于dp[ i ],用二分查找的方法确定。按顺序从左到右,计算每个数字作为序列的最后一位,就能构成最长的序列。

    -------------------------------------------------------PS:灵魂画师已上线--------------------------------------------------

    接下来图示一波:

    现在给出14个数:13 7 9 16 38 24 37 18 4 19 21 22 63 15

    变量声明:tail表示已记录的最长序列长度,最后直接输出即可。

                    low为下届,high为上界。

    DP过程如下【只介绍前两个的过程】:

    初始情况:

    

    第一次二分:

    

    第二次二分:

    

    剩下的以此类推,因为全部扫一遍,复杂度为O(N),对每个元素都要二分,复杂度O(log N),所以总的复杂度为O(N log N).。

    下面贴代码:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;

int n,a,mid,dp[MAXN];
int main()
{
	dp[0]=0;
	scanf("%d",&n);
	int tail=0;
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&a);
		int low=0,high=tail-1;
		while(low<=high)
		{
			mid=(low+high)>>1;
			if(dp[mid]>=a)
			  high=mid-1;
			else
			  low=mid+1;	
		}
		if(low>=tail)
		  tail++;
		dp[low]=a;
	}
	printf("%d",tail);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/g21glf/article/details/80996958