今早起来想起了拦截导弹问题,就又想到了最长上升子序列,前段时间学习过这个算法,但可能由于太久没练了,忘记了到底是个怎么原理,今天就打算写下来,以后就可以翻出来看看。
问题描述
上升子序列是在一串序列中,要使a1 < a2 < a3 < …… < ak,但在这里,是子序列,那就意味着他们不需要相临,但需要保持与原序列中相对的顺序是相同的。而在前面,我写了一篇kmp算法,那里是字符串匹配,那就需要是连续的,匹配到的序列应当与子串的对应起来,而在这里我们就了解如何去求最长上升子序列。
三种解法(目前先更新两种:dp和二分)
eg: 序列: 1 3 2 5 3 6
我们在这里将dp[i]记作以a[i]结尾的最长上升子序列
前1个数 a[1]=1,dp[1]=1;
前2个数 a[2]=3,a[2] > a[1],dp[2]=2;
前3个数 a[3]=2,a[3] > a[1],a[3] < a[2],dp[3]=dp[1]+1=2;
前4个数 a[4]=5,a[4] > a[1],a[4] > a[2],a[4] > a[3] ,dp[4]=dp[3]+1=3;
前5个数 a[5]=3,a[5] > a[1],a[5] > a[3],dp[5]=dp[3]+1=3;
前6个数 a[6]=6,a[6] > a[1],a[6] > a[2],a[6] > a[3],a[6] > a[4],a[6] > a[5],dp[6]=dp[5]+1=4;
在这里得出,这个最长上升子序列的元素数是4,但也可以看出最长上升子序列的选择是不唯一的,但我们可以在这里得出最长上升子序列的个数。
-代码
1 | #include<stdio.h> |
二分法求最长上升子序列
在一串序列中,我们也可以采用替换法来求最长上升子序列,eg;我们可以用一个数组来装子序列,然后在后面的时候,去替换前面第一个比他大的位置的数。
这里处理我们需要一个最长子序列的数组_long[],用len来记录它的长度,记得初始化全部为inf(0x3f3f3f3f).
eg:1 3 2 5 3 6
len=0;
首先a[1]=1,a[1] < _long[1] (inf),_long[1]=1,len++,len=1;
a[2]=3,a[2] < _long[2] (inf),_long [2] =3,len++,len=2;
a[3]=2,a[3] < _long[2] (3),_long[2]=2,len=2;
a[4]=5,a[4] < _long[3] (inf),_long[3]=5,len++,len=3;
a[5]=3,a[5] < _long[3] (5),_long[3]=3,len=3;
a[6]=6,a[6] < _long[4] (inf),_long[4]=6,len++,len=4;
在代码处理中,寻找_long[] 中的大于等于a[i]的数可以用c++中的lower_bund();
- 代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<math.h>
#include<string.h>
using namespace std;
int main()
{
int a[6]={1,3,2,5,3,6};
int _long[6];
int i,j;
memset(_long,0x3f3f3f3f,sizeof(_long));
int len=0;
for(i=0;i<6;i++)
{
j=lower_bound(_long,_long+6,a[i])-_long+1;
_long[lower_bound(_long,_long+6,a[i])-_long]=a[i];
if(len<j)
len=j;
}
printf("%d\n",len);
return 0;
}