最长上升子序列(含例题)

<span style="font-size:14px;">int d[maxn],g[maxn];
int s[maxn],k;
int ans;
void increase(){//求s[0,1...k-1]中最长上升子序列
    mem(d,0),mem(g,0x3f),ans=0;
    for(int i=0;i<k;i++){
        int l=lower_bound(g+1,g+k+1,s[i])-g;
        d[i]=l,g[l]=s[i],ans=max(ans,d[i]);
    }
}

void not_down(){//求s[0,1...k-1]中最长不降子序列
    mem(d,0),mem(g,0x3f),ans=0;
    for(int i=0;i<k;i++){
        int l=upper_bound(g+1,g+k+1,s[i])-g;
        d[i]=l,g[l]=s[i],ans=max(ans,d[i]);
    }
}<pre name="code" class="cpp">//注:求最长下降子序列(不升)只需将原序列反向转化为求最长上升子序列/不降子序列即可。若直接求需要手写二分函数,不能直接调用lower_bound和upper_bound,这两个函数只能在上升序列中使用,因为对于相等的情况返回值都是最小的下标值</span>

 

uva10534

求已知序列的最长波浪子序列(不一定连续),满足:长度为2*k+1,前k个数递增,后k个数递减

dp1[i]:以a[i]结尾的最长递增子序列 

dp2[i]:以a[i]开头的最长递减子序列

ans=max{ 2*min(dp1[i],dp2[i])-1 }

<span style="font-size:14px;">#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define maxn 10010
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;

int n,wave[maxn],rev[maxn];
int d1[maxn],d2[maxn],tmp[maxn];
int g[maxn],ans;

void increase(int s[],int d[]){
    mem(d,0),mem(g,0x3f);
    for(int i=0;i<n;i++){
        int l=lower_bound(g+1,g+n+1,s[i])-g;
        d[i]=l,g[l]=s[i];
    }
}

int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%d",&n)!=EOF){
        for(int i=0;i<n;i++) { scanf("%d",&wave[i]); rev[n-1-i]=wave[i]; }
        increase(wave,d1); increase(rev,d2);
        for(int i=0;i<n;i++) tmp[i]=d2[n-1-i];
        for(int i=0;i<n;i++) d2[i]=tmp[i];
        ans=0;
        for(int i=0;i<n;i++){
            int cur=min(d1[i],d2[i]);
            ans=max(ans,2*cur-1);
        }
        printf("%d\n",ans);
    }
}
</span>

hdu 5141  

求有多少个连续子序列ai,a(i+1)...aj,它的LIS与原序列的LIS长度相等

对g数组方法的应用。

g[i]:长度为i的上升子序列中第i个数的最小值(值相同是取较大下标的值)

性质:g[i]也是第i个数下标最大的值

反证法:如果存在i<j,a[i]<a[j],d[i]=d[j],又d[j]>=d[i]+1,矛盾!

设cnt[i]:长度为i的上升子序列中第1个数下标的最大值

性质:cnt[ ]是递减的

证明:,任何一个长度为n的上升子序列a[i1,i2...in]都包含一个长度为l-1的上升子序列a[i2,i3...in],a[i2]的下标大于a[i1]的下标,因此cnt[n-1]>=cnt[n]

如何求cnt[ ]?

结论:长度为i的上升子序列中第一个数下标的最大值一定在第i个值取得最小值时取得

证明:设i<j,a[i]>a[j],d[i]=d[j],即以i和以j为结尾的LIS长度相同,但是a[i]>a[j]。若以a[i]结尾的LIS的第一位数下标大于一a[j]结尾的LIS的第一位数下标,那么以a[i]结尾的LIS长度还可以继续增大,与d[i]=d[j]矛盾!所以结论成立。


由上述结论可得,在二分插入值时,可以用g数组中前一个数的cnt值求后一个数的cnt值。

结果只需要用到目前最大的cnt值,并且更新更大的cnt值时,也只需要用到之前最大的cnt值,因此只需要保证最大的cnt值正确即可。若最大的cnt值正确,即cnt[l-1]已知,则cnt[l]=cnt[l-1]。

证明:由上述结论和g[l-1]<g[l]可知,cnt[l-1]的第一个数下标的最大值一定可以当做cnt[l]的第一个下标的最大值。若存在cnt[l]>cnt[l-1],则cnt[l-1]则会增大,与cnt[l-1]的正确性相悖,矛盾!


<span style="font-size:14px;">#include<stdio.h>
#include<iostream>
#include<string.h>
#define ll long long
#define INF 0x3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
#define maxn 100100
using namespace std;

ll n,h1[maxn];
ll g[maxn],cnt[maxn],ans,maxlen;

void increase(){
    mem(cnt,0),mem(g,0x3f),maxlen=0,ans=0;
    for(ll i=0;i<n;i++){
        ll l=lower_bound(g+1,g+n+1,h1[i])-g;
        if(l==1) cnt[l]=i;
        else cnt[l]=cnt[l-1];
        if(l>maxlen) ans=(cnt[l]+1);
        else ans+=(cnt[maxlen]+1);
        g[l]=h1[i]; maxlen=max(maxlen,l);
    }
}

int main(){
    //freopen("a.txt","r",stdin);
    while(scanf("%lld",&n)!=EOF){
        for(ll i=0;i<n;i++) scanf("%lld",&h1[i]);
        increase();
        printf("%lld\n",ans);
    }
}
</span>


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值