最长上升子序列

示例题目:POJ2533
参考书籍:《挑战程序设计竞赛》

第一种方法:O(n^2)
dp[i]:以a[i]结尾的最长上升子序列的长度

  • 只包含a[i]的序列
  • 由a[j]追加a[i]得到(满足j< i并且a[j]< a[i])

状态转移方程:
dp[i]=max{dp[i],dp[j]+1}

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[1005];
int dp[1005];

int main()
{
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        memset(dp,0,sizeof(dp));
        int res=0;
        for(int i=1;i<=n;i++)
        {
            dp[i]=1;
            for(int j=1;j<i;j++)
            {
                if(a[i]>a[j])
                    dp[i]=max(dp[i],dp[j]+1);
            }
            res=max(dp[i],res);
        }
        cout<<res<<endl;
    }
    return 0;
}

第二种方法:O(nlogn)
dp[i]:长度为i+1的上升子序列中末尾元素的最小值(不存在就是INF)
状态转移方程:
对数列从左往右扫一遍,对于当前扫到的元素a[j]
如果i=0或者dp[i-1]< a[j],就用dp[i]=min(dp[i],a[j])更新,
考虑到dp数列中除INF外是单调递增的,dp[i]的指针可由
lower_bound(dp,dp+n,a[j])得出。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int a[1005];
int dp[1005];
/// dp[i]表示长度为i+1的上升子序列中末尾元素的最小值
/// 不存在就是INF

int main()
{
    //int t[5]={1,2,4,4,5};
    //cout<<lower_bound(t,t+5,3)-t<<endl;
    ios::sync_with_stdio(false);
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
        {
            cin>>a[i];
        }
        fill(dp,dp+n,INF);
        for(int i=0;i<n;i++)
        {
            *(lower_bound(dp,dp+n,a[i]))=a[i];
        }
        cout<<lower_bound(dp,dp+n,INF)-dp<<endl;
    }
    return 0;
}

按照白书上的理解

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int a[1005];
int dp[1005];//dp[i]:以a[i]结尾的LIS的长度
int g[1005];//g[k]=a[j]   (dp[j]=k&&j最小)
///g[1]<=g[2]<=g[3]<=...<=g[n]  (if not exist g[i]=INF)
///dp[i]=max(0,dp[j])+1   (j<i&&a[j]<a[i])

int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0;i<n;i++)
            cin>>a[i];
        fill(g,g+n+1,INF);
        for(int i=0;i<n;i++)
        {
            //g[k']<a[i]<=g[k]   (k=k'+1)
            int k=lower_bound(g+1,g+n+1,a[i])-g;
            //g[k']=a[j']<a[i]   dp[j']=k'=k-1
            //g[k]=a[j]>=a[i]
            dp[i]=k;
            //dp[i]=max(0,dp[j'])+1=k  (j'<i&&a[j']<a[i])
            g[k]=a[i];
        }
        cout<<lower_bound(g+1,g+n+1,INF)-(g+1)<<endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值