挑战LIS (二分 加 正反遍历)



对于一个数的序列a1,a2...an,当满足a1<a2<...<an时,我们定义这样的序列是严格递增(上升)的序列。我们还可以从这个序列中抽出部分数字(至少1个)
并按原有顺序排列,组成新的序列,我们称之为一个子序列。
最长上升子序列(Longest Increasing Subsequence,LIS)是一个非常经典的问题,它是指一个序列中最长且严格递增的子序列。
对于求一个已知序列的LIS的长度,我们有一个非常易懂的方法。
设原有序列a的长度为n,定义f[i]表示从a[1]至a[i],且以a[i]为结尾最长上升子序列的长度,
则f[i](1<=i<=n)可以由以下的C++代码计算
for (int i=1; i<=n; i++) {
    int maxValue=0;
    for (int j=1; j<=i-1; j++) {
        if (a[j]<a[i]&&f[j]>maxValue) {
            maxValue=f[j];
        }
    }
    f[i]=maxValue+1;

}

最终在f[i] (1<=i<=n)中取最大值,我们便可以得到LIS的长度。

现在,我们不止对最长上升子序列的长度感兴趣,并且想要知道对于原有序列中的每个数是否属于某个最长上升子序列。

输入格式

多组输入数据
每组数据第一行为一个数n,(1<=n<=999)表示原序列的长度

下一行为n个整数(在int范围内),分别是a1至an。


输出

每组数据分为两行输出,第一行为一个整数L,表示原序列的最长上升子序列的长度。

第二行为n个连续的数字,若原序列某个数字不属于任意最长上升子序列,则输出数字1;若属于某个最长上升子序列则输出数字2。


样例输入

3
1 2 3
4
3 4 2 5
5
111 16 -73 11 9

样例输出

3
222
3
2212
2
11222



#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<queue>
#include<math.h>
#include<stack>
using namespace std;
#define LL long long
#define mp(a,b) make_pair((a),(b))
#define clr(x,a) memset(x,a,sizeof(x))
#define INF 0x3f3f3f3f
#define lb(x) ((x)&(-x))
#define rep(i,a,b) for(int i=a;i<=b;i++)
const int N=1005,siz=1e9;
const int MOD=1e9+7;
int n;
int gcd(int x,int y){
    return y==0?x:gcd(y,x%y);
}
int a[1005];
int f[1005],g[1005],dp[1005];
int solve(int len,int x){
    int l=1,r=len+1;

    int ret;
    while(l<=r){
        int m=(l+r)>>1;
        if(l==len+1) return len+1;
        if(x>=dp[m]){
            ret=m;
            r=m-1;

        }else{
            l=m+1;
        }


    }
    return ret;
}
int main()
{
    //freopen("aaa.txt","r",stdin);
    //freopen("bbb.txt","w",stdout);

    while(~scanf("%d",&n)){
        for(int i=0;i<n;i++){
          scanf("%d",&a[i]);

        }
        clr(f,0);
        clr(g,0);
        clr(dp,0);
        f[0]=1;
        int len=1;
        dp[1]=a[0];
        for(int i=1;i<n;i++){
            f[i]=lower_bound(dp+1,dp+len+1,a[i])-dp;
            dp[f[i]]=a[i];
            if(f[i]>len) len=f[i];
        }
        int ans=len;
        g[n-1]=1;
        clr(dp,0);
        dp[1]=a[n-1];
        len=1;
        for(int i=n-2;i>=0;i--){
            g[i]=solve(len,a[i]);
            if(g[i]>len) len=g[i];
            dp[g[i]]=a[i];

        }
//       for(int i=0;i<n;i++) cout<<f[i];
//        cout<<endl;
//        for(int i=0;i<n;i++) cout<<g[i];
//        cout<<endl;
        cout<<len<<endl;
        for(int i=0;i<n;i++){
            if(g[i]+f[i]==ans+1) cout<<2;
            else  cout<<1;

        }
        cout<<endl;

    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值