赞
踩
最长上升子序列就是求:给定的一串数字中 找出不断上升的最长那一串(该串不一定相连,只保证递增即可)。就比如下面这个例子 其最长上升子序列为2 3 4 7或2 3 4 6 数串长度为4 那么具体怎么求的呢
我们拿一个最简单的例子讲:
【题目描述】
给定N个数,求这N个数的最长上升子序列的长度。
【样例输入】
7
2 5 3 4 1 7 6
【样例输出】
4
一、朴素算法 (复杂度O(n^2))
dp[i]的值为max(dp[j]+1,dp[i])也就是:
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
a[i] | 2 | 5 | 3 | 4 | 1 | 7 | 6 |
dp[i] | 1 | 2 | 2 | 3 | 1 | 4 | 4 |
没看懂表格没关系,下面是具体解释 对a[i]从头开始遍历,第一个数是2,由于2前面没有数字,则到2的最大长度就是1(其本身)即dp[0]=1。接着a[1]=5。a[1]>a[0].那么这时候dp[2]=dp[1]+1=2; a[2]=3,往前遍历3<5不符合条件,3>2满足dp[3]=dp[1]+1=2;a[3]=4,往前遍历找满足条件的最大值 4>3,dp[3]=dp[2]+1=3,4<5不符合条件,4>2 dp[1]+1=2<(dp[3]=3),则dp[3]=3。以此类推 。。。
下面上代码:
- #include<stdio.h>
- #include<iostream>
- #include<algorithm>
- using namespace std;
- int main(){
- int n,dp[100010],a[100010];
- int i,j,maxn;
- while(~scanf("%d",&n)){
- for(i=0;i<n;i++){
- scanf("%d",&a[i]);
- }
- memset(dp,0,sizeof(dp));
- for(i=0;i<n;i++){
- dp[i]=1;
- for(j=i;j>=0;j--){//往前遍历,找到以a[i]结尾的最大值
- if(a[i]>a[j]){
- dp[i]=max(dp[i],dp[j]+1);
- }
- }
- }
- maxn=0;
- for(i=0;i<n;i++){//遍历dp[i]找到最大值
- if(maxn<dp[i]){
- maxn=dp[i];
- }
- }
- printf("%d\n",maxn);
- }
- return 0;
- }
二、二分法(复杂度nlogn)
用dp[i]表示数字串长度i的时对应的最小末尾值。然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len记录目前最长算到多少了
还是以上面的题举例
首先,把a[1]有序地放到dp里,令dp[1] = 2,表示当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时的Len = 1。
接着,a[2] = 5,a[2] > dp[1],所以令dp[1+1] = dp[2] = a[3] = 5,表示长度为2的LIS的最小末尾是5,这时候dp[1..2] = 2, 5,这时的Len = 2。
接着,a[3] = 3,它正好夹在了1和5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是把5淘汰掉,这时候dp[1..2] = 2, 3,这时Len = 2。
继续,a[4] = 4,它在3后面,因为dp[2] = 3, 而4在3后面,于是很容易可以推知dp[3] = 4, 这时dp[1..3] = 2, 3, 4,这时的Len = 3。
a[5] = 1,这时候往前遍历你会发现a[5]<dp[1],则这时 将dp[1]替换为a[5]即dp[1]=a[5]=1,此时dp[1..2]=1,3,4
a[6] = 7,它很大,比4大,于是dp[4] = 7,这时的Len = 4。
a[7] = 6,dp[3]<a[7]<dp[4]得到dp[4] = 6,这时的Len = 4。
注意。这个1,3,4,6不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。用STL写:
- #include<stdio.h>
- #include<string.h>
- #include<algorithm>
- using namespace std;
- #define INF 0x3f3f3f
- int dp[10010];//dp[i]表示长度为i+1的子序列末尾元素最小值;
- int a[10010];
- int main()
- {
- int n;
- while(scanf("%d",&n)!=EOF)
- {
- for(int i=0;i<n;i++)
- {
- scanf("%d",&a[i]);
- dp[i]=INF;//不可以用memset对数组赋值INF,只能赋值0或-1;
- //可以用fill(dp,dp+n,INF);
- }
- for(int i=0;i<n;i++)
- {
- *lower_bound(dp,dp+n,a[i])=a[i];//找到>=a[i]的第一个元素,并用a[i]替换;
- }
- printf("%d\n",lower_bound(dp,dp+n,INF)-dp);//找到第一个INF的地址减去首地址就是最大子序列的长度;
- }
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。