您的位置:首页 > 其它

最长上升子序列问题

2015-09-29 14:14 253 查看
 最长上升子序列问题
有一个长为n的数列请求出这个序列中最长的上升子序列的长度,上升子序列值的是对于任意的,都满足的子序列。

N的范围决定与算法的选择

1<=N<=10000

这个问题也被称为最长递增子序列(LIS)

首先建立递推关系:

定义

dp[i]:=以为末尾的最长递增子序列的长度

以结尾的上升子序列是:

只包含的子序列

在满足并且的以为结尾的上升序列末尾,追加后得到的子序列

这二者之一,就能得到如下的递推公式:



代码:

 

//输入
int n;
int a
;
int dp
;

void solve()
{
int res=0;
for(int i=0; i<n; ++i)
{
dp[i]=1;
for(int j=0; j<i; ++j) if(a[j]<a[i])
{
dp[i]=max(dp[i],dp[j]+1);
}
res=max(res,dp[i]);
}
printf("%d\n",res);
}</span>


此外还可以定义其他递推关系,前面我们利用DP求取针对最末位的元素的最长递增子序列,如果子序列的长度相同

那么最末位的元素较小的在之后会更有优势,所以可以考虑反过来用DP针对相同长度情况下最小的末位元素进行求解

dp[i]:=长度为的上升序列中末位元素的最小值(不存在的话为INF)

  用DP来更新这个数组:

最开始全部DP数组赋值INF ,然后由前到后逐个考虑数列的元素,对于每个,如果i==0,或者

的话,就用dp[i]=min(dp[i],)进行更新,最终找出使得dp[i]<INF的最大的i+1

就是结果,此方法为考虑进一步优化,首先dp数组中除INF之外是单调递增的,所以对于每个最多需要更新1次,对于更新位置可以进行二分搜索进行加速,复杂度O(nlogn)。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int b
,c
;
int num
;
int ans
;
int LIS1(const int *a,int n)
{
b[1]=a[0];
int len=1;
for(int i=1; i<n; ++i)
{
int k=lower_bound(b+1,b+len+1,a[i])-b;
if(k>len) len++;
b[k]=a[i];
}
return len;
}
int LIS2(const int *a,int n)
{
int len=1,Left,Right,mid;
c[1]=a[0];    ///初始时,最大递增子序列长度为1的最末元素为a1
for(int i=1; i<n; i++)
{
Left=1;   ///左边初始长度为1
Right=len;///右边表示当前所求长度
while(Left<=Right)///二分查找最末元素小于ai+1的长度最大的最大递增子序列;
{
mid=(Left+Right)/2;
if(c[mid]<a[i]) Left=mid+1;
else Right=mid-1;
}
c[Left]=a[i];///将长度为Left的最大递增子序列的当前最末元素置为ai+1;
if(Left>len) len++;///更新当前最大递增子序列长度;
}
return len;
}


普通O(n^2)

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
int dp[105];//用dp[i]保存长度为i的单调子序列的末尾元素的值,用len保存单调子序列的最大长度。
int arr[105];
int main()
{
int _,n,m;
scanf("%d",&_);
while(_--)
{
memset(dp,0,sizeof(dp));
scanf("%d",&n);
for(int i=0; i<n; ++i)
scanf("%d",&arr[i]);
int len=1;
dp[1]=arr[0];
for(int i=1; i<n; ++i)
{
if(arr[i]>dp[len])
{
dp[++len]=arr[i];
}
else{
int j;
for(j=len-1; j>=1; --j)
if(arr[i]>dp[j])
break;
dp[j+1]=arr[i];
}
}
printf("%d\n",len);
}
return 0;
}二分搜索优化O(nlogn)
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}
int dp[100005];
int arr[100005];
int LIS[100005];//记录路径
int Binary_search(int l,int r,int x)
{
while(l<=r){
int mid=(l+r)>>1;
if(dp[mid]==x)
return mid;
else if(dp[mid]<=x)
l=mid+1;
else r=mid-1;
}
return l;
}
void init()
{
memset(dp,inf,sizeof(dp));
memset(arr,0,sizeof(arr));
memset(LIS,0,sizeof(LIS));
}
int main()
{
int n;
while(~scanf("%d",&n))
{
init();
for(int i=1; i<=n; ++i)
scanf("%d",&arr[i]);
int len=1;
int ans=1;
for(int i=1; i<=n; ++i)
{
int ck=Binary_search(1,len,arr[i]);
if(ck==len) len++;
dp[ck]=arr[i];
LIS[i]=ck;
ans=max(ans,ck);
}
printf("%d\n",ans);
for(int i=n; i>=1; --i)
{
if(LIS[i]==ans)
{
printf("%d",arr[i]);
if(len!=1) putchar(' ');
ans--;
}
}
} return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: