本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

最长单调子序列(DP)

问题描述:给定某一组无序的数,求其中最长的单调序列的长度(该单调序列不一定是连续的),如在 5  6  1  2  4  7  5 中最长上升增序列是1 2 4 7 或者1 2 4 5。

该题可以用动态规划的方法解决,时间复杂度为O(n^2):

设A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度。

动态规划方程:F[t] = max{1, F[j] + 1}   ,其中( j = 1,  2, ..., t - 1, 且A[j] =< A[t] )。   (注意:这篇文章我们假设是非严格单调上升,如果严格作单调上升则为:A[j] < A[t],下同)

通过以上公式求得最长上升子序列的长度为max( F(t) )。

printResultSeq函数通过递归的方法来打印出所有的最长子序列。

参考代码:

 1 //seq:保存当前已经计算好的序列,(从后往前计算);
 2 //len:最长子序列的长度;
 3 //tmpresult:当前要求得子序列长度;
 4 //end:在F中查找长度为tmpresult的位置,查找范围为[0,end);
 5 //A:原始序列;
 6 //F :对应正文算法分析中的F;
 7 void printResultSeq(int A[],int F[],int end,int tmpresult,int seq[],int len)
 8 {
 9     for(int i=0;i<end;i++)
10     {
11         if(tmpresult==F[i] && A[i]<=seq[tmpresult+1])
12         {
13             seq[tmpresult]=A[i];
14             if(tmpresult==1)
15             {
16                 for(int k=1;k<=len;k++)
17                     cout<<seq[k]<<",";
18                 cout<<"end"<<endl;
19                 return;
20             }
21             
22             printResultSeq(A,F,i,tmpresult-1,seq,len);
23 
24         }
25     }
26 }
27 
28 int fun( int A[] ,int n)
29 {
30     int *F=new int[n+1];
31     int result=0;
32     F[0]=1;
33     for(int i=1;i<n;i++)
34     {
35         F[i]=1;
36         for(int j=0;j<i;j++)
37         {
38             if(A[i]>=A[j])
39                 F[i]=max(F[i], F[j]+1);
40         }
41         if(result<F[i])
42             result=F[i];
43     }
44 
45     //////////////////print all result
46     int *seq=new int[result+2];
47     seq[result+1]=numeric_limits<int>::max(); //the max of int 
48     printResultSeq(A, F, n, result,seq,result);
49 
50     return result;
51 }

 

---------------------------------------------------------------------------

下面是改进后的O(nlogn)的算法:

我们仔细考虑计算F[t]时的情况。假设有两个元素A[x]和A[y],满足 
(1)x < y < t

(2)A[x] < A[y] < A[t]

(3)F[x] = F[y]

此时,选择F[x]和选择F[y]都可以得到同样的F[t]值,那么,在最长上升子序列的这个位置中,应该选择A[x]还是应该选择A[y]呢?

很明显,选择A[x]比选择A[y]要好。因为由于条件(2),在A[x+1] ... A[t-1]这一段中,如果存在A[z],A[x] < A[z] < a[y],则与选择A[y]相比,将会得到更长的上升子序列。 

从以上的分析中我们得出一个结论:

如果我们知道A[0:t-1]的最长单调上升子序列的长度为 k,以及所有长度为k的上升子序列中结尾元素的最小值(记为b[k]),我们的关键是维护这个数组b[]

那么 如果A[t]>=b[k], 则 A[0:t]的最长单调上升子序列的长度为k+1,且b[k+1]=A[t] ;         (结论1)

       如果A[t]<b[k], 则 A[0:t]的最长单调上升子序列的长度为k:若A[t]<b[1],则b[1]=A[t],

                                                                                    若b[1]=< A[t] =<b[k] ,由“结论1”可知数组 b 是有序的,用二分法插之后到下标 j 使得b[j-1]=<A[t]<b[j],把b[j] 的值变为A[t]

参考代码:

 1 int binary(int b[],int k,int key)
 2 {
 3     if(key<b[1])return 1;
 4     int low=1,high=k;
 5     while(low!=high-1)
 6     {
 7         if( b[k=(low+high)/2] <= key )low=k;
 8         else high=k;
 9     }
10     return high;
11 }
12 
13 int fun2(int A[] ,int n)
14 {
15     int *b=new int[n+1],k=1;
16     b[1]=A[0];
17     for(int i=1;i<n;i++)
18     {
19         if(A[i]>=b[k])b[++k]=A[i];
20         else b[binary(b,k,A[i])]=A[i];
21     }
22     return k;
23 }

 对于该算法怎么打印所有结果还没有研究???????

 

还有另一种做法,先对原数组排序得到A1,然后求A和A1的最长公共子序列

 

可参考编程之美2.16 求数组中最长递增子序列

 【版权声明】转载请注明出处 http://www.cnblogs.com/TenosDoIt/archive/2013/04/19/3031589.html

posted @ 2013-04-19 22:04  tenos  阅读(868)  评论(0编辑  收藏  举报

本博客rss订阅地址: http://feed.cnblogs.com/blog/u/147990/rss

公益页面-寻找遗失儿童