**
最长不下降子序列
**
题目描述
一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < i;K <=N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8). 你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
输出
最长上升子序列的长度。
这道题呢,很显然是很普通的最长不下降子序列问题,只求长度即可。
所以思路也很简单,就是从头遍历,用一个一维数组 f [ i ] 来存储从第一个元素
到第 i 个元素的最长不下降子序列。复杂度 O(n^2);
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,ans,a[10005],f[10005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[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);
}
}
ans=max(ans,f[i]);
}
printf("%d",ans);
return 0;
}
这里可以看到按照这种写法在循环内部是一个一个元素遍历来找的,这里可以用 upper_bound ( ) 或者二分来优化,复杂度降到 O( n *log n );
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,ans=1,a[10005],b[10005];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
b[1]=a[1];
for(int i=2;i<=n;i++){
if(a[i]>=b[ans]){
b[++ans]=a[i];
}
else{
int j=upper_bound(b+1,b+ans+1,a[i])-b;
b[j]=a[i];
}
}
printf("%d",ans);
return 0;
}
这是求最长不下降子序列的长度,如果要求输出求出的最长不下降子序列该怎么办呢。
要记录这个最长的上升序列,那么 n*logn 的 非 dp 做法似乎不行,这里用 O(n^2)的 dp 来写;
首先,用 f [ i ] 记录从 1 到 i 的最长上升序列长度,赋初值为 1 ;
用 b [ i ] 记录以 a [ i ] 为结尾的子序列的前一个元素位置;
当 a [ j ] < a [ i ] ,则当前的 a [ i ] 成为已知的以a [ j ] 结尾的最长序列的最新一位元素。
f [ i ] = max ( f [ i ] , f [ j ] + 1 );
如果更新了 f [ i ] 的值,则得到的序列是目前最长的序列;i 的前一位是 j ,表示为 b [ i ] = j ;记录下当前i的位置,表示为 times = i ;用 sum 记录序列最长长度 ;最后输出长度 sum ;
递归输出答案序列:
先找 cwy ( times ),表示从最后一位开始输出,一直向 b [ x ] 递归下去,直到 x = 0 ,回溯输出答案。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int f[100005],a[100005],b[100005];
//记录这个最长的上升序列,那么nlogn做法不行,用 n^2 dp ;
//f[i]表示到从a[1]到a[i]最长不下降子序列的长度;
//b[i]表示以a[i]结尾的子序列的前一个元素的位置;
int sum,n,times;
void cwy(int x){
//递归输出;
if(x==0){
return;
}
//因为是从最后一个开始找的,所以要先递归,都找到后倒着再输出;
else{
cwy(b[x]);
printf("%d ",a[x]);
}
}
int main()
{
while(scanf("%d",&a[++n])!=EOF);
n--;
for(int i=1;i<=n;i++){
f[i]=1;
}
//初值为 1 ;
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(a[i]>a[j]){
//a[i]可以成为已知以a[j]结尾的序列的最新一位元素;
if(f[i]<f[j]+1){
f[i]=f[j]+1;
b[i]=j;
// i的前一位是j,表示为 b[i]=j;
if(sum<f[i]){
//sum记录序列最长长度;
sum=f[i];
times=i;
//记录当前 i 的位置;
}
}
}
}
}
printf("max=%d\n",sum);
cwy(times);
//递归输出;
return 0;
}
这样就能输出最长不下降子序列了。
谢谢阅读。
本题样例输入:
300 250 275 252 200 138 245
样例输出:
max=2
250 275