最长不下降子序列LIS

LIS
题解:
最长不下降子序列,英文缩写为 LIS(Longest Increasing Subsequence)。其
定义是,设有由 n 个不相同的整数组成的数列,记为:
a(1)、a(2)、……、a(n)且 a(i)<>a(j) (i<>j)
例如 3,18,7,14,10,12,23,41,16,24。
若存在 i1 降序列。如上例中 3,18,23,24 就是一个长度为 4 的不下降序列,同时也有 3,7,10,
12,16,24 长度为 6 的不下降序列。程序要求,当原数列给出之后,求出最长的不下降序
列。
基本算法:
dp[i]表示以 a[i]结尾,能构成的最长不下降子序列长度。
初始: dp[i]=1;
转移: dp[i]=max(dp[j]+1) (1<=j 因为 dp[i]表示以 a[i]为结尾,能构成的最长不下降子序列长度,所以求最终答案的
时候,还需要枚举 i,这样的做法时间复杂度是 O(n^2)。
优化算法:
例如数据为 4 7 3 5 8 2 6,我们可以定义另外一个辅助数组 h[i],表示当构造长
度为 i 上升序列时第 i 个数字最小值,这样,对刚才的数据,我们首先 h[1]=4,h[2]=7;
当扫描到 3 时,这个时候发现当构成一个长度的上升序列时,尾数越小越好,因为之后可
接数的可能性越大,因此 h[1]要更新为 3;当扫描到 5 时,同理,h[2]=5;扫描到 8 时,
h[3]=8;扫描到 2 时,h[1]=2;扫描到 6 时 h[3]=6,如下所示:
h[]={4, 7}
h[]={3, 7}
h[]={3, 5}
h[]={3, 5, 8}
h[]={2, 5, 8}
h[]={2, 5, 6}
这个辅助数组 h 最终长度即为最长上升序列的长度,并且我们发现辅助数组内的元素
时递增的,所以当我们要维护这个辅助数组时,有如下策略:
if(a[i]>h[len]) h[++len]=a[i]
else {
int p=search(1,len,a[i]);
//此处在 h 数组中二分搜索大于等于 a[i]的最小数值的下标
h[p]=a[i];
}
也就是说,维护 h 数组,我们只需要 logn 的时间复杂度,那么总的复杂度可以降低为
O(nlogn)。
代码1:

/*#include
using namespace std;
int n,height[105],a_greater[105],a_less[105],res;
int find1(int l,int r,int x){
	int ret=r;
	while(l<=r){
		int mid=(l+r)/2;
		if(a_greater[mid]>=x){
			ret=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return ret;
}
int find2(int l,int r,int x){
	int ret=l;
	while(l<=r){
		int mid=(l+r)/2;
		if(a_less[mid]<=x){
			ret=mid;
			l=mid+1;
		}
		else l=mid+1;
	}
	return ret;
}
void LIS_greater(){
	for(int i=2;i<=n;i++){
		for(int j=1;jheight[j]) a_greater[i]=max(a_greater[i],a_greater[j]+1);
	}
}
void LIS_less(){
	for(int i=n-1;i>=1;i--){
		for(int j=i+1;j<=n;j++)
			if(height[i]>height[j]) a_less[i]=max(a_less[i],a_less[j]+1);
	}
}
void optimized_LIS_greater_dichotomy(){

}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&height[i]);
		a_greater[105]=a_less[105]=1;
	}
	//合唱队形
	LIS_greater();
	LIS_less();
	for(int i=1;i<=n;i++) res=max(res,a_greater[i]+a_less[i]-1);
	printf("%d\n",n-res);
	//严格递增序列
	return 0;
}*/
#include
using namespace std;
typedef long long ll;
int up[105],down[105];
int n,a[105];
int main() {
	scanf("%d",&n);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]), up[i]=down[i]=1;
	for(int i=2; i<=n; i++) {
		for(int j=1; j<i; j++) {
			if(a[i]>a[j])
				up[i]=max(up[i], up[j]+1);
		}
	}
	for(int i=n-1; i>=1; i--) {
		for(int j=i+1; j<=n; j++) {
			if(a[i]>a[j])
				down[i]=max(down[i], down[j]+1);
		}
	}
	int res=0;
	for(int i=1; i<=n; i++) {
		res=max(res,up[i]+down[i]-1);
	}
	printf("%d\n",n-res);
	return 0;
}

代码2:

#include
using namespace std;
int n,a[100005],h[100005],ht;
int find(int st,int ed,int v) {
	int ret=ed;
	while(st<=ed) {
		int mid=(st+ed)/2;
		if(h[mid]>=v) {
			ret=mid;
			ed=mid-1;
		} else st=mid+1;
	}
	return ret;
}
int main() {
	scanf("%d",&n);
	for(int i=1; i<=n; i++) {
		scanf("%d",&a[i]);
		a[i]-=i;
	}
	h[ht=1]=a[1];
	for(int i=2; i<=n; i++) {
		if(a[i]>=h[ht]) h[++ht]=a[i];
		else {
			int p=find(1,ht,a[i]);
			h[p]=a[i];
		}
	}
	printf("%d\n",n-ht);
	return 0;
}

你可能感兴趣的:(题解,c++,动态规划)