求LIS的两种方法:DP 与 二分法 ~~

LIS(Longest Increasing Subsequence):最长上升子序列

这里分两种情况来看:

  1. 子序列严格递增(即子序列中不能存在相等)
  2. 子序列非降(即子序列中可以存在相等)

对于序列:a[1] , a[2] , a[3] , … , a[N]


一、动态规划(DP)   时间复杂度:O(N2)

  1. 子序列严格递增:dp[i] = max{1 , dp[j]+1}   ( j
  2. 子序列非降:dp[i] = max{1 , dp[j]+1}   ( j

即找到能衔接在a[i]前最大的dp[j] (a[j]能衔接在a[i]之前)




二、二分法   时间复杂度:O(N*logN)

创建一个数组d(下标从1开始): 维护LIS的长度

一个变量len: 表示当前d的长度,d[len]正好是LIS的末尾元素,最终得到的len即为答案。

初始令 d[1]=a[1],len=1; 再从i=2开始遍历a

注意,数组d中仅仅只是长度与LIS相同,本身并不是一个LIS(因为插入后前后顺序被打乱了),只能得到LIS的长度,若要得到LIS,还需要记录路径。
详见—> https://blog.csdn.net/Ratina/article/details/98875528

1. 子序列严格递增

d[len] < a[i]:说明a[i]能接在d[len]后面,长度增加,直接 d[++len] = a[i];

否则,找到 d(LIS)中第一个大于等于a[i]的元素 d[k],令 d[k] = a[i];

若d[k] > a[i],a[i]更小,替换以后能后面能接的元素更多。
若d[k]==a[i],因为序列单调递增,不能存在相等元素,所以替换。


2. 子序列非降

d[len] <= a[i]:说明a[i]能接在d[len]后面,长度增加,直接 d[++len] = a[i];

否则,找到 d(LIS)中第一个大于a[i]的元素 d[k],令 d[k] = a[i];

若d[k] > a[i],a[i]更小,替换以后能后面能接的元素更多。
因非降序列允许元素重复,所以要保留重复元素。


接下来介绍STL的两个函数:(利用二分法,时间复杂度为O(logN) )

下界函数:lower_bound(first , last , v)
找到并返回 非降序列 [first,last) 中第一个大于等于v的元素的地址

上界函数:lower_bound(first , last , v)
找到并返回 非降序列 [first,last) 中第一个大于v的元素的地址
求LIS的两种方法:DP 与 二分法 ~~_第1张图片
此外,上述两函数的第四个参数为比较函数(可省略)

	//子序列严格递增
	int len=1;
	d[1]=a[1];
	for(int i=2;i<=N;i++)
	{
		if(d[len]<a[i])
			d[++len]=a[i];
		else
			*lower_bound(d+1,d+len+1,a[i])=a[i];
	}
	cout<<len;
	//子序列非降
	int len=1;
	d[1]=a[1];
	for(int i=2;i<=N;i++)
	{
		if(d[len]<=a[i])
			d[++len]=a[i];
		else
			*upper_bound(d+1,d+len+1,a[i])=a[i];
	}
	cout<<len;

你可能感兴趣的:(★动态规划,★二分,★Tips,#,【基础DP】)