您的位置:首页 > 其它

动态规划之最长子序列问题

2017-04-04 20:04 183 查看
一个数的序列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).

我们的任务,就是对于给定的序列,求出最长上升子序列的长度。(最长不上升子序列就是正相反呗。)

下面我们以最长上升子序列为例研究。

其他非递增(减)等一系列问题就是以此为基础啊,比较的时候改改条件就行啊!

看清题目要求!

递增和不下降是不一样的啊摔!

O(n²)算法

seq[i]表示输入的第i个元素,seqlen[i]表示以第i个元素为末位的最长子序列长度。

在seqlen数组里选出最长,就是最长子序列长度啊。

O(nlogn)算法

arr[i]表示输入的数组元素,ans[len]表示生成长度为len时子序列的末位元素,len表示序列长度。

如果arr数组的新元素比ans数组的所有元素都大,把这个元素放在ans数组的最后,子序列长度+1;

如果arr数组的新元素比ans数组的最大元素小,比ans数组的其他元素小,那就用新元素替换掉这个最大元素。比如len=2时,ans[2]={1,5};若ans[i]即新元素为3,那就替换掉5,使得ans[2]={1,3},以保证有更多元素加入到序列中来。

所以我们发现,我们插入这个数据是通过替换来实现的,因而我们可以通过二分查找快速找到要替换的元素,从而将时间复杂度降低到O(nlogn)。

来举个栗子~

假设存在一个序列arr[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。

下面一步一步试着找出它。

我们定义一个序列ans,然后令 i = 1 to 9 逐个考察这个序列。

此外,我们用一个变量Len来记录现在最长算到多少了

首先,把arr[1]有序地放到ans里,令ans[1] = 2,就是说当只有一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1。

然后,把arr[2]有序地放到ans里,令ans[1] = 1,就是说长度为1的LIS的最小末尾是1,arr[1]=2已经没用了,这时Len=1。

接着,arr[3] = 5,arr[3]>ans[1],所以令ans[1+1]=ans[2]=arr[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候ans[1..2] = 1, 5,Len=2。

再来,arr[4] = 3,它正好夹在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候ans[1..2] = 1, 3,Len = 2。

继续,arr[5] = 6,它在3后面,因为ans[2] = 3, 而6在3后面,于是很容易可以推知ans[3] = 6, 这时ans[1..3] = 1, 3, 6, Len = 3 。

第6个, arr[6] = 4,它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len=3。

第7个, arr[7] = 8,它很大,比4大,于是ans[4] = 8。Len=4。

第8个, arr[8] = 9,得到ans[5] = 9,Len继续增大,L=5。

最后一个, arr[9] = 7,它在ans[3] = 4和ans[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

ATTENTION!这个1,3,4,7,9不是LIS(真正的LIS是1,3,4,8,9或2,5,6,8,9),它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。

好啦,来个模板题→_→模板水题POJ 2533

Description

A numeric sequence of ai is ordered if a1 < a2 < … < aN. Let the subsequence of the given numeric sequence (a1, a2, …, aN) be any sequence (ai1, ai2, …, aiK), where 1 <= i1 < i2 < … < iK <= N. For example, sequence (1, 7, 3, 5, 9, 4, 8) has ordered subsequences
bde4
, e. g., (1, 7), (3, 4, 8) and many others. All longest ordered subsequences are of length 4, e. g., (1, 3, 5, 8).

Your program, when given the numeric sequence, must find the length of its longest ordered subsequence.

Input

The first line of input file contains the length of sequence N. The second line contains the elements of sequence - N integers in the range from 0 to 10000 each, separated by spaces. 1 <= N <= 1000

Output

Output file must contain a single integer - the length of the longest ordered subsequence of the given sequence.

Sample Input

7

1 7 3 5 9 4 8

Sample Output

4

O(n²)算法

#include<cstdio>
#include<algorithm>
const int MAX=1010;
using namespace std;
int seq[MAX];
int seqlen[MAX];
int main()
{
int i,j,n;
int m;
int maxn,maxlen=1;
while(~scanf("%d",&n))
{
for(i=1; i<=n; i++)
{
seqlen[i]=1;
scanf("%d",&seq[i]);
}
for (i=2; i<=n+1; i++)
{
maxn=0;
for (j=1; j<=i-1; j++)
{
if(seq[j]<seq[i]&&seqlen[j]>maxn)
maxn=seqlen[j];
}
seqlen[i]=maxn+1;
maxlen=max(maxlen,seqlen[i]);
}
printf("%d\n",maxlen);
}
return 0;
}


O(nlogn)算法

#include<cstdio>
#include<iostream>
#include<cstring>
const int MAXN=1010;
int arr[MAXN],ans[MAXN],i;
int seek(int num,int len)
{
int low=1,high=len;
while(low<=high)
{
int mid=(low+high)/2;
if(num>=ans[mid]) low=mid+1;
else high=mid-1;
}
return low;
}
int lis(int n)
{
ans[1]=arr[1];
int len=1;
for(i=2; i<=n; ++i)
{
if(arr[i]>ans[len])
ans[++len]=arr[i];
else
{
int pos=seek(arr[i],len);
ans[pos] = arr[i];
}
}
return len;
}
int main()
{
int T;
while(~scanf("%d",&T))
{
for(i=1; i<=T; ++i)
scanf("%d",&arr[i]);
printf("%d\n",lis(T));
}
return 0;
}


ps:

好几天前看过的了,当时被韬神说的无地自容(虽然也没说什么○| ̄|_),现在序列还是心中的痛/(ㄒoㄒ)/~~

不过现在好几分会了QAQ
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: