【最长递增子序列】
给定数列A1,A2,...An,求最长递增子序列
输入:
第一行一个整数n,表示有n个数(n<=1000)
第二行n个整数,用空格隔开。
输出:
最长递增子序列长度。
【分析】
在求以Ai为末元素的最长递增子序列时,找到所有序号在Ai前面且小于Ai的元素Aj,即j<i且Aj<Ai。如果这样的元素存在,那么对所有Aj,都有一个以Aj为末元素的最长递增子序列的长度S(j),把其中最大的S(j)选出来,那么S(i)就等于最大的S(j)加上1,即以Ai为末元素的最长递增子序列,等于以使S(j)最大的那个Aj为末元素的递增子序列最末再加上Ai;如果这样的元素不存在,那么Ai自身构成一个长度为1的以Ai为末元素的递增子序列。
阶段i:以第i个数为末元素
状态S[i]:以第i个数为末元素的可能递增序列长度
转移方程:S[i+1]←max{S[i]}+1
【算法】
S[1]:=1; {以第一个元素为末元素的递增序列长度肯定是1}
For i←2 to n do
For j←1 to i-1 do
Begin
搜索A[i]前面比A[i]小的数A[j],得到对应的S[j];
S[i]←max{S[j]}+1;
End;
Write(max{S[i]});
【参考程序】
#include<stdio.h>
int main()
{
intd[10],s[10];
intlen,i,j,w;
while(scanf("%d",&len),len)
{
for(i=0;i<len;i++)scanf("%d",d+i);
s[0]=1;
for(i=1;i<len;i++)//s[0...len-1]
{
w=0;
for(j=0;j<i;j++)if(d[j]<d[i])
{
if(s[j]>w) w=s[j];
}
s[i]=w+1;
}
w=0;
for(i=0;i<len;i++)if(s[i]>w)w=s[i];
printf("%d\n",w);
}
return0;
}
最长递增子序列问题的不同求解方法求解
最长递增子序列问题是一个很基本、较常见的小问题,但这个问题的求解方法却并不那么显而易见,需要较深入的思考和较好的算法素养才能得出良好的算法。由于这个问题能运用学过的基本的算法分析和设计的方法与思想,能够锻炼设计较复杂算法的思维,我对这个问题进行了较深入的分析思考,得出了几种复杂度不同算法,并给出了分析和证明。
一, 最长递增子序列问题的描述
设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值。
二, 第一种算法:转化为LCS问题求解
设序列X=<b1,b2,…,bn>是对序列L=<a1,a2,…,an>按递增排好序的序列。那么显然X与L的最长公共子序列即为L的最长递增子序列。这样就把求最长递增子序列的问题转化为求最长公共子序列问题LCS了。
最长公共子序列问题用动态规划的算法可解。设Li=< a1,a2,…,ai>,Xj=<b1,b2,…,bj>,它们分别为L和X的子序列。令C[i,j]为Li与Xj的最长公共子序列的长度。则有如下的递推方程:
这可以用时间复杂度为O(n2)的算法求解,由于这个算法上课时讲过,所以具体代码在此略去。求最长递增子序列的算法时间复杂度由排序所用的O(nlogn)的时间加上求LCS的O(n2)的时间,算法的最坏时间复杂度为O(nlogn)+O(n2)=O(n2)。
三, 第二种算法:动态规划法
设f(i)表示L中以ai为末元素的最长递增子序列的长度。则有如下的递推方程:
这个递推方程的意思是,在求以ai为末元素的最长递增子序列时,找到所有序号在L前面且小于ai的元素aj,即j<i且aj<ai。如果这样的元素存在,那么对所有aj,都有一个以aj为末元素的最长递增子序列的长度f(j),把其中最大的f(j)选出来,那么f(i)就等于最大的f(j)加上1,即以ai为末元素的最长递增子序列,等于以使f(j)最大的那个aj为末元素的递增子序列最末再加上ai;如果这样的元素不存在,那么ai自身构成一个长度为1的以ai为末元素的递增子序列。
这个算法由Java实现的代码如下:
public void lis(float[] L)
{
intn = L.length;
int[]f = new int[n];//用于存放f(i)值;