删除最少的元素 LIS

LIS复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)的算法:

int ans[MAX_N],a[MAX_N],dp[MAX_N],n; //a是数组序列,ans 用来保存每个dp值对应的最小值 
int len; //LIS 最大值
ans[1] = a[1] ;
len =1;
for(int i=2;;i++){
	if(a[i]>ans[len]){
		ans[++len] = a[i];
	}
	else{
		int pos = lower_bound(ans+1,ans+len+1,a[i]) -ans;
		ans[pos] =a[i];
	}
} 
cout<<len<<endl; //len 就是LIS的值 

LIS:由贪心的思想,用dp[len]保存长度为len的子序列中结尾最小的那个值。比如第i个位置的数为x,它与那些1~i-1中的结尾小于x的子序列构成新的子序列。遍历dp,找到第一个大于x的位置pos(知 x > d p [ p o s − 1 ] , x < d p [ p o s ] x>dp[pos-1],x<dp[pos] x>dp[pos1],x<dp[pos]),更新其值为x,即对应长度的子序列结尾值被更新(变得更小)。
LIS的各种变体对应解决策略 O ( n 2 ) O(n^2) O(n2)

  • 最长上升子序列 lower_bound()
  • 最长不上升子序列 dp数据乘以-1,upper_bound()
  • 最长下降子序列 dp数据乘以-1,lower_bound()
  • 最长不下降子序列 upper_bound()

STL二分搜索:
头文件 #include< algorithm>
upper_bound(first,last,val):返回从first到last的左闭右开区间中第一个大于 val的位置。
lower_bound(first,last,val):返回从first到last的左闭右开区间中第一个大于等于 val的位置。

方法1

r [ 1 ] ∼ r [ i ] r[1]\sim r[i] r[1]r[i]复杂度为 O ( n 2 ) O(n^2) O(n2), r [ i ] ∼ r [ n ] r[i]\sim r[n] r[i]r[n]复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

#include<iostream>
#include<stdio.h> 
#include<string.h>
#include<algorithm>
using namespace std;
int dp[1010];
int r[1010];
int main(){
	//freopen("in.txt","r",stdin);
	int n;
	cin>>n;
	int index = 0;
	for(int i = 1;i<=n;i++){
		cin>>r[i];
	}
	int M = 0;
	int ans = 0;
	for (int i = 1;i<=n;i++){
		ans = 0;
		memset(dp,0,sizeof(dp)); 
		for(int p =1;p<=i;p++){ // 计算从 r[1]~ r[i] 的最长不上升子序列 
			dp[p] = 1;//  单个节点也可视为长为1的序列,初始化为1  
			for(int q = 1;q<p;q++){
				if(r[p]<=r[q]){
					dp[p] = max(dp[p],dp[q]+1);
				}
			}
			ans = max(ans,dp[p]);
		}
		//cout<<i<<'\n';
		//cout<<ans<<'\n';
		int len = 1; // LIS 最大值 
		dp[i+len] =r[i];
		for(int p = i+1;p<=n;p++){ //计算 从 r[i]~ r[n]的最长不下降子序列 
			if(r[p]>=dp[i+len]){
				dp[++len+i] = r[p];
			}
			else{
				int pos = upper_bound(dp+i+1,dp+i+len+1,r[p])-dp;
				dp[pos] = r[p];
			}
		}
		//cout<<len<<"\n\n";
		if(len+ans-1>=M){
			M =len+ans-1;
		}
	}
	cout<<n-M<<"\n";
	return 0;
}

方法2

总体复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

#include<iostream>
#include<stdio.h> 
#include<string.h>
#include<algorithm>
using namespace std;
int dp[1010];
int r[1010];
int main(){
	//freopen("in.txt","r",stdin);
	int n;
	cin>>n;
	int index = 0;
	for(int i = 1;i<=n;i++){
		cin>>r[i];
	}
	int M = 0;
	int ans = 0;
	for (int i = 1;i<=n;i++){
		memset(dp,0,sizeof(dp));
		ans = 0;
		int len =1;
		dp[len] = -r[1]; 
		for(int p =2;p<=i;p++){ // 计算从 r[1]~ r[i] 的最长不上升子序列 
			if(r[p]<=-dp[len]){
				dp[++len] = -r[p];
			}
			else{
				int pos = upper_bound(dp+1,dp+len+1,-r[p])-dp;
				dp[pos] = -r[p];
			}
		}
		ans = len;
		len = 1; // LIS 最大值 
		dp[i+len] =r[i];
		for(int p = i+1;p<=n;p++){ //计算 从 r[i]~ r[n]的最长不下降子序列 
			if(r[p]>=dp[i+len]){
				dp[++len+i] = r[p];
			}
			else{
				int pos = upper_bound(dp+i+1,dp+i+len+1,r[p])-dp;
				dp[pos] = r[p];
			}
		}
		//cout<<len<<"\n\n";
		if(len+ans-1>=M){
			M =len+ans-1;
		}
	}
	cout<<n-M<<"\n";
	return 0;
}

方法3

总体复杂度为 O ( n 2 ) O(n^2) O(n2)

#include<iostream>
#include<stdio.h> 
#include<string.h>
#include<algorithm>
using namespace std;
int dp[1010];
int r[1010];
int main(){
	freopen("in.txt","r",stdin);
	int n;
	cin>>n;
	int index = 0;
	for(int i = 1;i<=n;i++){
		cin>>r[i];
	}
	int M = 0;
	int ans = 0;
	for (int i = 1;i<=n;i++){
		ans = 0;
		memset(dp,0,sizeof(dp)); 
		for(int p =1;p<=i;p++){
			dp[p] = 1;//  单个节点也可视为长为1的序列,初始化为1  
			for(int q = 1;q<p;q++){
				if(r[p]<=r[q]){
					dp[p] = max(dp[p],dp[q]+1);
				}
			}
			ans = max(ans,dp[p]);
		}
		int len = 0;
		for(int p = i;p<=n;p++){
			dp[p] =1;
			for(int q =i;q<p;q++){
				if(r[p]>=r[q]){
					dp[p] = max(dp[p],dp[q]+1);
				}
			}
			len = max(len,dp[p]);
		}
		if(len+ans-1>=M){
			M =len+ans-1;
		}
	}
	cout<<n-M<<"\n";
	return 0;
}

方法4

在这里插入图片描述
官方推荐做法,总体复杂度为 O ( n 2 ) O(n^2) O(n2),
引用出处

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int a[1010];
int dp2[1010];
int dp1[1010];
int n;
int main()
{   
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    memset(dp1,0,sizeof(dp1));
    for(int i=1;i<=n;i++)
    {dp1[i]=1;
        for(int j=1;j<i;j++)
        {
            if(a[j]>=a[i])dp1[i]=max(dp1[i],dp1[j]+1);
        }
    }
    memset(dp2,0,sizeof(dp2));
    for(int i=n;i>=1;i--)
    {dp2[i]=1;
        for(int j=n;j>i;j--)
        {
            if(a[j]>=a[i])dp2[i]=max(dp2[i],dp2[j]+1);
        }
    }
    int ans[1010];
    for(int i=1;i<=n;i++)
        {
            ans[i]=n-(dp1[i]+dp2[i]-1);
        }
    int minn=0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
            if(minn>ans[i])minn=ans[i];
        }
    cout<<minn;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiu_cs

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值