最长不下降序列
查看 提交 统计 提问
总时间限制: 1500ms 内存限制: 65536kB
描述
设有由n个整数组成的数列,任意删掉若干后剩下的数列成为子序列。
如果子序列是严格不递减的,则成为不下降子序列。
求该数列的最长不下降子序列的长度。
并输出一组一组符合要求的最长子序列。
输入
两行
第一行n(1≤n≤10000)
第二行n个数,用空格隔开,范围均在(-1000到1000之间)
输出
两行
第一行:最长子序列长度
第二行:符合要求的一组最长子序列,两个数之间用空格隔开,行末不含空格
样例输入
14
3 7 9 16 38 24 27 38 44 49 21 52 63 15
样例输出
11
3 7 9 16 24 27 38 44 49 52 63
提示
dp
思路点拔:首先,本题是一道经典dp,不管是最长不下降序列,还是什么序列,状态转移方程都是f[i]=max(f[j]+1,f[i]),只是判断条件不相等,我就不全列举了,本题的判断条件为v[i]>=f[j],这样,长度就能对,但是,如何还原一组正解呢?就是从最后一位开始倒退,这样说有点抽象,我们亮一下倒推的代码吧!!
g[1]=v[t]; //赋初值
for(int i=2;i<=n;i++) //外层2~n做循环
{
for(int j=t;j>1;j--) //内层循环从最长长度开始倒退
{
if(v[j-1]<=v[t]&&f[j-1]==f[t]-1)
{
g[i]=v[j-1]; //推出一个正解元素
t=j-1;//修改t,继续查找下一个正解元素
break;//结束内层循环
}
}
}
倒推代码出来了,相信用状态转移方程推出最长不下降序列的长度,就完事了~
上完整代码!!!
#include<cstdio>
#include<iostream>
using namespace std;
int v[10005],f[10005]={0,1},g[10005],n,t,maxn;
int main()
{
scanf("%d",&n); //输入n
for(int i=1;i<=n;i++)//输入n个元素
{
scanf("%d",&v[i]);
}
for(int i=2;i<=n;i++)
{
f[i]=1; //赋初值
for(int j=1;j<i;j++)
{
if(v[i]>=v[j]) //判断条件
{
f[i]=max(f[j]+1,f[i]); //dp(状态转移方程)
}
}
}
for(int i=1;i<=n;i++)
{
if(f[i]>maxn)
{
maxn=f[i]; //计算最长不下降序列的长度
t=i; //将t的值赋值为i
}
}
g[1]=v[t]; //赋初值
for(int i=2;i<=n;i++) //外层2~n做循环
{
for(int j=t;j>1;j--) //内层循环从最长长度开始倒退
{
if(v[j-1]<=v[t]&&f[j-1]==f[t]-1)
{
g[i]=v[j-1]; //推出一个正解元素
t=j-1;//修改t,继续查找下一个正解元素
break;//结束内层循环
}
}
}
printf("%d\n",maxn);
for(int i=maxn;i>=1;i--) //输出
{
printf("%d",g[i]);
if(i!=1)//养成好习惯,处理一下行末的空格
printf(" ");
}
printf("\n");//换行
return 0; //完美结束
}