【代码记录】LIS最长上升子序列简单思路+模板

首先区分子序列和子串的区别,子串必须是连续的,子序列可以是不连续的

例题  https://www.luogu.com.cn/problem/P1020

算法一:贪心加二分实现(时间复杂度 : n*log(n))

函数:

lower_bound(ForwardIter first, ForwardIter last,const _Tp& val)算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置。

upper_bound(ForwardIter first, ForwardIter last, const _Tp& val)算法返回一个非递减序列[first, last)中的第一个大于值val的位置。

函数内部实现是二分。

简单思路:建一个数组a来存储当前长度为i时最长上升子序列的最小的尾值,因为对于LIS来说他的尾值越小越有利于其变的更长。当当前数大于数组a的尾值时,直接加入,当当前数小于尾值时,从数组中找出第一个大于等于当前数的值,将其替换。

代码:

int len=0;
for(int i=1;i<n;i++)
{
    int pos=lower_bound(f, f+len, a[i]) - f;
    len=max(len,pos+1);
    f[pos]=a[i];
}
cout <<len<<endl;
 //上升
 
 
int len=0;
for(int i=1;i<n;i++)
{
    int pos=upper_bound(f, f+len, a[i], greater<int>()) - f;
//更新序列结尾第一个小于a[i]的序列
    len=max(len,pos+1);
    f[pos]=a[i];
}
cout <<len<<endl;
//下降

greater<int>() c++中的大于函数

算法二: DP+树状数组优化

 简单思路:设数组dp[i]表示 长度为i以a[i]结尾的序列的最长上升子序列长度

                   状态转移方程 : dp[i]=max(dp[i],dp[j]+1);

但是对于朴素的DP,需要每次都枚举前i个数去找,时间复杂度达到(n*n)需要用树状数组来优化每次找比他小的数的位置这一过程。

struct Node{
    int val,num;
}z[maxn]; 
bool cmp(Node a,Node b)
{
    return a.val==b.val?a.num<b.num:a.val<b.val;
}
void modify(int x, int y)
//把val[x]替换为val[x]和y中较大的数 
{
    for(; x<=n; x+=x&(-x))
        T[x] = max(T[x],y);
}
int query(int x)
//返回val[1]~val[x]中的最大值 
{
    int res=-INF;
    for(; x; x-=x&(-x))
        res=max(res,T[x]);
    return res;
}
for(int i=1; i<=n; i++)
   {
        scanf("%d", &z[i].val);
        z[i].num = i; 
    }
    sort(z+1, z+n+1, cmp);
	//以权值从小到大排序 
    for(int i=1; i<=n; i++)
	//按权值从小到大枚举 
    {
        int maxx = query(z[i].num);
		//查询编号小于等于num[i]的LIS最大长度
        modify(z[i].num, ++maxx);
		//把长度+1,再去更新前面的LIS长度
        ans=max(ans, maxx);
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值