您的位置:首页 > 运维架构

竞赛题目讲解-【Northeastern Europe 2002, Far-Eastern Subregion】最长上升子序列

2017-08-18 13:33 183 查看

【Northeastern Europe 2002, Far-Eastern Subregion】最长上升子序列

Description

一个数的序列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).

你的任务,就是对于给定的序列,求出最长上升子序列的长度。

Input

输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。

Output

最长上升子序列的长度。

Sample Input

7
1 7 3 5 9 4 8


Sample Output

4


题目解析

这是一个动态规划的题,按照常规,作者还是先介绍搜索(Time Limit Exceeded)的做法。首先的输入无论是什么做法都是一样的,相信作者也不必讲解了。接下来就是寻找答案了,这里注意一个区别——子串是连续的,而子序列是可以断开的。与寻找子串相同,子序列可以从原串的任意位置开始,于是我们用for循环枚举每一个可能的子序列起点。然后我们需要写一个函数,来求出以原串的某位置为子序列的第一个元素的所有子序列中长度最长的子序列的长度。函数(flag)参数表如下:

int x:当前子序列末尾的元素在原串中的位置
int tot:当前子序列的长度


我们可以在进入函数时就更新答案(ans)的值为当前找到的子序列的长度的最大值,也就是
ans=max(ans,tot);
。由于是序列,那么其中的元素在原串中就一定是从前往后排列的,因此我们用for循环从当前末尾在原串中的后一个位置枚举到原串的尾部(x+1~n),来找出子序列的下一个元素。题目要求要“上升”,其实是给出了一个子序列的条件,于是我们在for循环查找下一个元素时需要判断该元素是否比当前序列的末尾大(严格大于),也就保证了上升。

当所有的子序列都找出来后,搜索也就完毕了,那么此时的ans也就是最终的答案了。

由于查找出所有子序列可能会有许多重复,因此容易超时。于是我们可以考虑进行记忆化,使得计算过的序列不再计算。因为我们的搜索是要找到最长的一个子序列,我们可以用一个数组(f)来储存,F[i]来表示在原序列中从开头到第i个元素这一段序列里以第i个元素为末尾的子序列的最长长度。为什么是以元素i为末尾呢?其实为开头还是末尾是不影响的,因为既然每个元素都会作开头,那么必然所有元素都可以做末尾。既然F[i]的定义中已经包含了第i个元素,那么子序列的长度至少为1(只有第i个元素),即F[i]的最小值为1。那么我们可以以1为初值,同样用for循环找到最大的长度。我们也需要对函数进行改变,现在flag有返回值了,那么需要把void改为int;因为F数组表示了长度,tot是不需要的,需要删去。其他部分与搜索相同。

题外话

这个是作者从草稿箱里翻出来写的,可能有些晚,见谅!

程序样例

Time Limit Exceeded-搜索:

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
using namespace std;
int n,xl[1005]={},ans=1;
void flag(int x,int tot)
{
ans=max(tot,ans);
for(int i=x+1;i<=n;i++)
if(xl[x]<xl[i])
flag(i,tot+1);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&xl[i]);
for(int i=1;i<=n;i++)
flag(i,1);
printf("%d\n",ans);
return 0;
}


Accepted-动态规划

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
using namespace std;
int main()
{
int n,xl[1005]={},f[1005]={};
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&xl[i]);
f[0]=1;
for(int i=1;i<n;i++)
{
int s=0;
for(int j=0;j<i;j++)
if(xl[j]<xl[i])
s=max(s,f[j]);
f[i]=s+1;
}
sort(f,f+n);
printf("%d\n",f[n-1]);
return 0;
}


Accepted-记忆化搜索

/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
using namespace std;
int s[1005],f[1005],n,ans=1;
int flag(int x)
{
if(f[x]) return f[x];
else
{
f[x]=1;
for(int i=0;i<x;i++)
if(s[i]<s[x])
f[x]=max(f[x],flag(i)+1);
return f[x];
}
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&s[i]);
f[0]=1;
for(int i=1;i<n;i++)
ans=max(flag(i),ans);
printf("%d\n",ans);
return 0;
}


The End

Thanks for reading

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ 算法竞赛 OpenJudge