• 最长单调子序列(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

  • 相关阅读:
    红帽中出现”This system is not registered with RHN”的解决方案
    消息队列
    安装nmap
    工作后才知道
    虚拟空间测速
    Update 两个表之间数据更新 (转)
    破解
    一步步打造自己的代码生成器
    Table轻松实现报表 转载
    sql使用convert转化
  • 原文地址:https://www.cnblogs.com/TenosDoIt/p/3031589.html
Copyright © 2020-2023  润新知