分享

动态规划

 雪柳花明 2016-10-10

引出:

问题描述:给出一个序列a1,a2,a3,a4,a5,a6,a7….an,求它的一个子序列(设为s1,s2,…sn),使得这个子序列满足这样的性质,s1<s2<s3<…<sn并且这个子序列的长度最长。输出这个最长的长度。(为了简化该类问题,我们将诸如最长下降子序列及最长不上升子序列等问题都看成同一个问题,其实仔细思考就会发现,这其实只是<符号定义上的问题,并不影响问题的实质)
例如有一个序列:1  7  3  5  9  4  8,它的最长上升子序列就是 1 3 4 8 长度为4.

分析:

这题目是经典的DP题目,也可叫作最长上升子序列或者 最长不下降子序列。有两种算法,复杂度分别为O(n*logn)和O(n^2) 。

算法1:
时间复杂度:O(n^2):
依次遍历整个序列,每一次求出从第一个数到当前这个数的最长上升子序列,直至遍历到最后一个数字为止,然后再取dp数组里最大的那个即为整个序列的最长上升子序列。我们用dp[i]来存放序列1-i的最长上升子序列的长度,那么dp[i]=max(dp[j])+1,(j∈[1, i-1]); 显然dp[1]=1,我们从i=2开始遍历后面的元素即可。

  1. <span style="color:#3366ff;">int dp[1000];  
  2. int LIS(int arr[1000], int n)  
  3. {  
  4.     for(int i=1; i<=n; ++i)  
  5.         dp[i] = 0;  
  6.     int ans;  
  7.     dp[1] = 1;  
  8.     for(int i=2; i<=n; ++i)  
  9.     {  
  10.         ans = dp[i];  
  11.         for(int j=1; j<i; ++j)  
  12.         {  
  13.             if(arr[i]>arr[j] && dp[j]>ans)  
  14.                 ans = dp[j];  
  15.         }  
  16.         dp[i] = ans+1;  
  17.     }  
  18.     ans = 0;  
  19.     for(int i=1; i<=n; ++i)  
  20.     {  
  21.         if(dp[i] > ans)  
  22.             ans = dp[i];  
  23.     }  
  24.     return ans;  
  25. }</span>  

算法2:
时间复杂度:(NlogN):
除了算法一的定义之外(p【i】相当于算法一中的arr【i】),增加一个数组b,b[i]用以表示长度为i最长子序列的最后一个数最小可以是多少。易证:i<j时,b[i]<b[j]。
在二分查找时,一直更新b[]内容,设此时b的总长度为k,
若1. arr[i] >= b[k], 则b[k+1] = arr[i];
若2. arr[i] <  b[k], 则在b[1..k]中用二分搜索大于arr[i]的最小值,返回其位置pos,然后更新b[pos]=arr[i]。

  1. <pre name="code" class="cpp"><span style="color:#3366ff;">// num为要查找的数,k是范围上限  
  2. // 二分查找大于num的最小值,并返回其位置  
  3. int bSearch(int num, int k)    
  4. {    
  5.     int low=1, high=k;    
  6.     while(low<=high)    
  7.     {    
  8.         int mid=(low+high)/2;    
  9.         if(num>=b[mid])    
  10.             low=mid+1;    
  11.         else     
  12.             high=mid-1;    
  13.     }    
  14.     return low;    
  15. }    
  16.    
  17. int LIS()  
  18. {  
  19.     int low = 1, high = n;  
  20.     int k = 1;  
  21.     b[1] = p[1];  
  22.     for(int i=2; i<=n; ++i)  
  23.     {  
  24.         if(p[i]>=b[k])  
  25.             b[++k] = p[i];  
  26.         else  
  27.         {  
  28.             int pos = bSearch(p[i], k);  
  29.             b[pos] = p[i];  
  30.         }  
  31.     }  
  32.     return k;  
  33. }  
  34. </span></pre><span style="color:#3366ff"><br></span>  

以下是证明b[]的单调递增性:
b序列是严格递增的,即b[1] < b[2] < … < b[t]。
证明:
若b[i] >= b[i + 1],b[i + 1] 是长度为i+1的递增子序列的尾项的最小值,设此序列为x[1]..x[i+1],x[1]..x[i]即构成长度为i的递增子序列,x[i] < x[i+1] = b[i+1] <= b[i],与b[i]定义不符。


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多