最长上升子序列和 lowe_bound(以 [POJ-1631] 为例)

本文深入探讨了最长上升子序列问题的多种解决方案,包括超时的朴素DP方法及优化后的高效算法。通过对比不同实现方式,阐述了如何利用lower_bound函数在已排序序列中寻找插入位置,从而实现更快速的求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[POJ-1631]

最长上升子序列,相等的元素不能重复加入这个序列,只能上升,不能水平。

先说一下超时的代码

dp[i]  记录到 i 为止,最长的长度。

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int a[40005];

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        for(int i=0;i<n;i++)
            cin>>a[i];

        int res=0;
        int dp[40005];
        for(int i=0;i<n;i++)
        {
            dp[i]=1;
            for(int j=0;j<i;j++)
            if(a[i]>a[j]) dp[i]=max(dp[i],dp[j]+1); // i不动,但是dp[i]  一直在变,所以要用 max
            
            res=max(res,dp[i]);
        }
    cout<<res<<endl;
//        cout<<*max_element(dp,dp+n)<<endl;

    }


    return 0;

}

这个是AC的代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define inf 1000000007
using namespace std;
typedef   long long ll;

int a[40005],b[40005]={1};
int main ()
{
//    freopen("in.txt","r",stdin);
//    freopen("date.out","w",stdout);

   int t;
   cin>>t;
   while(t--)
   {

       int n;
       cin>>n;
       for(int i=0;i<n;++i)
           {cin>>a[i]; b[i]=1;}

           b[0]=a[0];
           int len=0;
       for(int i=1;i<n;++i)
       {
            if(a[i]>b[len])
                b[++len]=a[i];

            else
            {
                int j=lower_bound(b,b+len+1,a[i])-b;
                b[j]=a[i];
            }

       }
        cout<<len+1<<endl;

   }

    return 0;
}

另一种写法

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int a[40005];
int dp[40005];
const int INF=0x3f3f3f3f;
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        memset(dp,0x3f,sizeof dp);
        int n;
        cin>>n;
        for(int i=0;i<n;i++)
            cin>>a[i];
        for(int i=0;i<n;i++)
        *lower_bound(dp,dp+n,a[i])=a[i]; // 在dp 里面找到第一个大于等于 a[i] 的位置,并把a[i] 放在这个位置,代替以前的数
        // 以前的数要不是 INF 要不是 延长的序列的最后一个(不是最优的,,,不够小的)值,
        // 原数组是  1 1 2 3 6 4 5
        // dp里面 放 1 1 2 3 6
        //  在看 a[5]的时候 发现 4比6(dp 延伸的最后一位)小,可以把 4 放在这里,有利于 dp 的延伸。

        cout<<lower_bound(dp,dp+n,INF)-dp<<endl;


    }


    return 0;

}

总结一下:

第一种方法,n*n 的 是针对每个a[i] 来找到对应的序列长度

第二三种方法 是直接找到最长(最优)的序列。

 

接下来说一下lower_bound

#include<bits/stdc++.h>
using namespace std;
int main()
{
    cout<<"第一组"<<endl;
    int a[10]={1,2,2,8,8,8,30,78,83,100};
    cout<<*lower_bound(a,a+10,a[3])<<endl; //  这个值
    cout<<lower_bound(a,a+10,a[3])-a<<endl;// 他的下标

    cout<<"第二组"<<endl;

   int b[10]={100,83,78,30,8,8,8,2,2,1};
    cout<<*lower_bound(b,b+10,b[3])<<endl; //  这个值
    cout<<lower_bound(b ,b+10,b[3])-b<<endl;// 他的下标

    cout<<"第三组"<<endl;

    int c[10]={1,30,2,8,8,2,100,78,8,83};
    cout<<*lower_bound(c,c+10,c[3])<<endl; //  这个值
    cout<<lower_bound(c,c+10,c[3])-c<<endl;// 他的下标



    return  0;
}

输出:

第一组
8
3
第二组
2686728
10
第三组
100
6

Process returned 0 (0x0)   execution time : 0.131 s
Press any key to continue.

只有第一组数据对了,只能操作升序数组。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值