LIS最长上升子序列O(n^2)与O(nlogn)的算法

动态规划

最长上升子序列问题(LIS)。给定n个整数,按从左到右的顺序选出尽量多的整数,组成一个上升子序列(子序列可以理解为:删除0个或多个数,其他数的顺序不变)。例如序列1, 6, 2, 3, 7, 5,可以选出上升子序列1, 2, 3, 5,也可以选出1, 6, 7,但前者更长。选出的上升子序列中相邻元素不能相等。

最容易想到的办法就是用一个数组f[i]保存到达第i个数的LIS

初始化f[i]=1

更新 f[i]=max{f[j]+1,f[i]|a[j]<a[i],1<=j<i}

即在第i位置前的比i小的最大的LIS+1

时间复杂度O(n^2)

#include<cstdio>
#include<iostream>//vj1098
#define ll long long
#define _max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=105;
int n,a[N],ans;
int f[N],g[N];
int main()
{
    freopen("sample.in","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++)
    scanf("%d",&a[i]),f[i]=g[i]=1;
    for(int i=1;i<=n;i++)
     for(int j=1;j<i;j++)
      if(a[j]<a[i])
       f[i]=_max(f[i],f[j]+1);
    for(int i=n;i>=1;i--)
     for(int j=n;j>i;j--)
      if(a[j]<a[i])
       g[i]=_max(g[i],g[j]+1);
    for(int i=1;i<=n;i++)
    ans=_max(ans,f[i]+g[i]-1);
    cout<<n-ans;
    return 0;
}

从蓝书和网上学到了一种更高效的O(nlogn)的算法

大概思路如下

  d[i]表示以i结尾的最长的LIS的长度,则d[i]=max{0,d[j]|j<i,Aj<Ai}+1,最终答案是max{d[i]}。如果LIS中的元素可以相等,把小于号改成小于等于号即可。

  假如已经计算出两个状态a,b满足Aa<Ab,且d[a]=d[b],则对于后续所有状态i(即i>a且i>b)来说,a并不会比b差——如果b满足Ab<Aa的条件,a也满足。换句话说,如果我们只保留a,一定不会丢失最优解。

  这样,对于相同的d值,最需要保留A最小的一个。我们用g[i]表示d值为i的最小状态编号(如果不存在,g[i]定义为正无穷)。根据上推理可证明

  g[1]<=g[2]<=g[3]<=……<=g[n]

#include<cstdio>
#include<iostream>
#define ll long long
#define _max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=300005;
int n,k,a[N],b[N],o[N],ans,ma,mb;
int j,da[N],db[N],len,la,lb,mid;
int findpos(int *d,int l,int r,int key){
    while(l<=r){
        mid=(l+r)>>1;
        if(key>d[mid]){
            if(key<=d[mid+1])
                return mid;
            else l=mid+1;
        }else r=mid-1;
    }return 0;
}
int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++)    scanf("%d",o+i);
    for(int i=1;i<k;i++)    o[i]<o[k]?a[++la]=o[i]:la=la;
    for(int i=k+1;i<=n;i++)    o[i]>o[k]?b[++lb]=o[i]:lb=lb;
    da[1]=a[1],len=1,j=0;
    for(int i=2;i<=la;i++)da[a[i]>da[len]?++len:findpos(da,1,len,a[i])+1]=a[i];
    db[1]=b[1],len=1,j=0;
    for(int i=2;i<=lb;i++)db[b[i]>db[len]?++len:findpos(db,1,len,b[i])+1]=b[i];
    for(int i=la;i>=1;i--)da[i]?ans+=i,i=0:i=i;
    for(int i=lb;i>=1;i--)db[i]?ans+=i,i=0:i=i;
    cout<<ans+1;
    return 0;
}
时间: 2024-08-26 19:34:21

LIS最长上升子序列O(n^2)与O(nlogn)的算法的相关文章

hdu 5256 序列变换(LIS最长上升子序列)

Problem Description 我们有一个数列A1,A2...An,你现在要求修改数量最少的元素,使得这个数列严格递增.其中无论是修改前还是修改后,每个元素都必须是整数. 请输出最少需要修改多少个元素. Input 第一行输入一个T(1≤T≤10),表示有多少组数据 每一组数据: 第一行输入一个N(1≤N≤105),表示数列的长度 第二行输入N个数A1,A2,...,An. 每一个数列中的元素都是正整数而且不超过106. Output 对于每组数据,先输出一行 Case #i: 然后输出

POJ 1887 Testingthe CATCHER (LIS:最长下降子序列)

POJ 1887Testingthe CATCHER (LIS:最长下降子序列) http://poj.org/problem?id=3903 题意: 给你一个长度为n (n<=200000) 的数字序列, 要你求该序列中的最长(严格)下降子序列的长度. 分析:        读取全部输入, 将原始数组逆向, 然后求最长严格上升子序列就可以. 因为n的规模达到20W, 所以仅仅能用O(nlogn)的算法求.        令g[i]==x表示当前遍历到的长度为i的全部最长上升子序列中的最小序列末

POJ - 3903 Stock Exchange(LIS最长上升子序列问题)

E - LIS Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Description The world financial crisis is quite a subject. Some people are more relaxed while others are quite anxious. John is one of them. He is very concerned abo

lis(最长上升子序列) dp

lis(最长上升子序列) dp 求序列的lis,子序列可不连续 for(int i=1;i<=N;i++){ scanf("%d",&a[i]); dp[i]=1; } for(int i=2;i<=N;i++){ for(int j=1;j<i;j++){ if(a[j]<a[i]) dp[i]=max(dp[i],dp[j]+1); } } int ans=1; for(int i=1;i<=N;i++){ //注意并不是dp[N]最大,而是要

poj2533——lis(最长上升子序列), 线性dp

poj2533——lis(最长上升子序列), 线性dp Longest Ordered Subsequence Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 36143   Accepted: 15876 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given n

hdu 5421 小明系列问题——小明序列(LIS最长上升子序列)

1 /***************************************************** 2 题目: 小明系列问题——小明序列(hdu 4521) 3 链接: http://acm.hdu.edu.cn/showproblem.php?pid=4521 4 算法: LIS最长上升子序列 5 6 ******************************************************/ 7 #include<cstdio> 8 #include<

POJ 3903 Stock Exchange (LIS:最长上升子序列)

POJ 3903Stock Exchange (LIS:最长上升子序列) http://poj.org/problem?id=3903 题意: 给你一个长度为n (n<=100000) 的数字序列, 要你求该序列中的最长(严格)上升子序列的长度. 分析: 由于n的规模达到10W, 所以只能用O(nlogn)的算法求. 令g[i]==x表示当前遍历到的长度为i的所有最长上升子序列中的最小序列末尾值为x.(如果到目前为止, 根本不存在长i的上升序列, 那么x==INF无穷大) 假设当前遍历到了第j个

算法设计 - LCS 最长公共子序列&amp;&amp;最长公共子串 &amp;&amp;LIS 最长递增子序列

出处 http://segmentfault.com/blog/exploring/ 本章讲解:1. LCS(最长公共子序列)O(n^2)的时间复杂度,O(n^2)的空间复杂度:2. 与之类似但不同的最长公共子串方法.最长公共子串用动态规划可实现O(n^2)的时间复杂度,O(n^2)的空间复杂度:还可以进一步优化,用后缀数组的方法优化成线性时间O(nlogn):空间也可以用其他方法优化成线性.3.LIS(最长递增序列)DP方法可实现O(n^2)的时间复杂度,进一步优化最佳可达到O(nlogn)

动态规划模板1|LIS最长上升子序列

LIS最长上升子序列 dp[i]保存的是当前到下标为止的最长上升子序列的长度. 模板代码: int dp[MAX_N], a[MAX_N], n; int ans = 0; // 保存最大值 for (int i = 1; i <= n; ++i) { dp[i] = 1; for (int j = 1; j < i; ++j) { if (a[j] < a[i]) { dp[i] = max(dp[i], dp[j] + 1); } } ans = max(ans, dp[i]); }