总时间限制: 2000ms 内存限制: 65536kB
一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= 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。
最长上升子序列的长度。
样例输入
7
1 7 3 5 9 4 8
样例输出
4
来源
翻译自 Northeastern Europe 2002, Far-Eastern Subregion 的比赛试题
动态规划(dp)的入门经典题目
很容易得出状态转移方程为:
f[i]=max{f[1],f[2],f[3]...f[j]}+1 (0
for(int i=1;i<=n;i++) f[i]=1;
一定要把f数组全部初始化成1,如果只初始化f[1]为1的话
当样例为类似于:
5
12 1 2 3 4 5
的时候,正解应该是5;而只初始化f[1]的话,答案会是3.
#include
#include
using namespace std;
int n,a[1005],f[1005],ans=-1;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
f[i]=1;
}
for(int i=2;i<=n;i++)
for(int j=1;jif(a[j]1);
}
for(int i=1;i<=n;i++)
ans=max(ans,f[i]);
printf("%d",ans);
}
变量说明:将读入的序列存放到a数组中,开一个数组d,d[i]表示长度为i的上升子序列的末尾元素。整数len指向数组d最后一个元素的位置,所以最后len就是最长上升子序列的长度。
具体操作:边读入x(读入序列中的元素)边判断:x比数组d的末尾元素(d[len])大的话,就把x接到数组d的后面——d[len+1]=x,len++;如果x<=d[len](这里说的是最长上升子序列),查找d数组中第一个大于等于x的元素的位置(果断二分)j,并执行操作:d[j]=x;
注:这里的 x 可以理解为a[i].
d[1]=a[1];
int len=1;
1.如果是最长上升子序列的话,可以用stl里的lower_bound()函数
#include
int j=lower_bound(d+1,d+len+1,a[i])-d;
//lower_bound(begin,end,k)的作用是二分查找[begin,end)
//左闭右开的区间内第一个大于k的数的位置,并返回它的地址,
//所以要-d(数组名就是数组里第一个元素的地址)
2.如果是最长不下降子序列的话,就可以用stl里的upper_bound()函数,具体操作和上面的lower_bound()一致
关于stl里二分查找函数的详解见这里 ->戳
3.其他乱七八糟的东西..比如说最长不上升子序列 或者是 最长下降子序列,就手动二分查找吧!!
4.关于二分法的练习:
Openjudge 7940查找最接近的元素
以及此题的Ac代码:戳
#include
#include
using namespace std;
int n,a[1001],d[1001];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int len=1;
d[1]=a[1];
for(int i=2;i<=n;i++)
{
if(a[i]>d[len])
{
d[++len]=a[i];
continue;
}
int j=lower_bound(d+1,d+len+1,a[i])-d;
d[j]=a[i];
}
printf("%d",len);
}
c++里的调试实在是太辣鸡了!!于是附上边执行边输出执行过程的代码:
#include
#include
#include
using namespace std;
int n,a[1001],d[1001];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int len=1;
d[1]=a[1];
//printf("Step %d: add %d \n",len,d[len]);
//没错,注释掉的这几句就是过程的输出
for(int i=2;i<=n;i++)
{
if(a[i]>d[len])
{
d[++len]=a[i];
//printf("Step %d: add %d \n",len,d[len]);
//cout<<"Now the array is: "<" ";
//for(int k=1;k<=len;k++)
//printf("%d ",d[k]);
//cout<continue;
}
int j=lower_bound(d+1,d+len+1,a[i])-d;
//int bbb=d[j];
d[j]=a[i];
//printf("\nchange %d->%d\n",bbb,d[j]);
//cout<<"Now the array is: "<" ";
//for(int k=1;k<=len;k++)
//printf("%d ",d[k]);
//cout<<"\n\n";
}
printf("%d",len);
}
附上图片展示:
输入样例为8
389 207 155 300 299 170 158 65
(偷懒用了最长不上升子序列-luogu导弹拦截 的样例..)
关于NlogN算法的一篇特别好的讲解
戳
以及luogu上一道关于最长不上升子序列的题目
P1020 导弹拦截 .