一、问题描述
设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列S=<ak1,ak2,…,akm>,其中k1<k2<…<km且ak1<ak2<…<akm。求最大的m值。比如序列(1, 7, 3, 5, 9, 4, 8)的递增子序列包括(1, 7), (3, 4, 8)等等,最长递增子序列为(1, 3, 5, 8),长度为4。
二、解题思路
(1) 把a1,a2,…,an排序,假设得到a’1,a’2,…,a’n,然后求a与a’的最长公共子序列,这样总的时间复杂度为o(nlg(n))+o(n^2)=o(n^2);
(2) 动态规划的思路:O(n^2) (POJ 2533)
//动态规划的思路:
// 另设一辅助数组b,定义b[n]表示以a[n]结尾的最长递增子序列的长度,
// 则状态转移方程如下:b[k]=max(b[j] && a[j]<a[k],j<k)+1;
// 这个状态转移方程解释如下:在a[k]前面找到满足a[j]<a[k]的最大b[j],然后把a[k]接在它的后面,
// 可得到a[k]的最长递增子序列的长度,或者a[k]前面没有比它小的a[j],那么这时a[k]自成一序列,长度为1.
// 最后整个数列的最长递增子序列即为max(b[k], 0<=k<=n-1);
#include<stdio.h>
int main()
{
int i,j,n,max;
int a[1000],b[1000];
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
b[0]=1;
max=1;
for(i=1;i<n;i++)
{
b[i]=1;
for(j=0;j<i;j++)
{
if(a[i]>a[j]&&b[j]+1>b[i]) b[i]=b[j]+1;
}
if(b[i]>max) max=b[i];
}
printf("%d\n",max);
}
return 0;
}
(3)动态规划的思路:O(nlogn) (POJ 1631)
#include<stdio.h>
//二分查找
int BSearch(int c[],int len,int k)
{
int low=0,high=len,mid=len/2;
while(low<=high)
{
if(k>c[mid])low=mid+1;
else if(k<c[mid])high=mid-1;
else return mid;
mid=(low+high)/2;
}
return low;
}
//用一个变量len记录到目前为止所找出来的最长递增序列的长度。
//另外准备一个数组c[],用这个数组表示长度为j的递增序列中最后一个元素的值。
//在这里长度为j的递增序列不止一个,我们所要保存是那个最小的。
//为什么呢?因为最后一个元素越小,那么这个递增序列在往后被延长的机会越大。
//初始化c[0] = -1;len = 0;从第一个元素a[1]开始进行二分查找。
//当在c数组里面找到一个数比a[i]小,并且他的后面的数大于或等于a[i]则跳出。
//将a[i]添加到这个数的后面。输出low。
int main()
{
int i,j,n,max,test;
int a[40000],c[40000];
scanf("%d",&test);
while(test)
{
test--;
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
max=1;
c[0]=-1;
c[1]=a[0];
for(i=1;i<n;i++)
{
j=BSearch(c,max,a[i]); //c[j-1]<a[i]<=c[j]
c[j]=a[i]; // | | |
if(j>max)max=j; //high a[i] low
}
printf("%d\n",max);
}
return 0;
}