POJ3903 最长递增子序列

来源:互联网 发布:ug软件好学吗 编辑:程序博客网 时间:2024/05/02 02:13

最长递增子序列,传统方法用dp思想,状态转移方程如下:

   dp[i] = Max{dp[j] | j < i && A[j] < A[i] } + 1;

传统写法是求dp[i]时枚举所有小于i的j,然后找一个最大的,最后加1得到dp[i],此方法复杂度是O(n^2)


有一种优化到O(n logn)的方法是这样的:

用maxV辅助数组,

maxV[i] 代表长度为i的递增子序列的最大元素的最小值。

我们会发现该数组是有序的,证明如下:

证明:反证法,假设当i<j<=k,有MaxV[i]>=MaxV[j],那么存在一个长度为i的递增序列a1a2...ai,且ai是计算到阶段k时所有长度为i的递增序列中最大元素的最小值,以及长度为j的序列b1b2...bj且ai>=bj,由于i<j长度j的序列b1b2...bj包含一个子序列b1b2...bi,且bi<bj<=ai,即长度为i的递增子序列最大元素的最小值不是ai,矛盾。

所以可以用二分查找来找到最长的可以将A[i]接到后面的子序列

代码如下(poj3903)

#include <iostream>#include <cstdio>#include <cstdlib>#include <vector>using namespace std;int LongestIncreasingSequence(const vector<long long>& array){      int n = array.size();      vector<int> maxV(n+1,0);      maxV[1] = array[0];      int ret = 1;      for(int i = 1; i < n; ++i){          int maxv = 1;          int left = 1, right = ret;          while(left <= right){              int mid = left + (right - left) / 2;              if(maxV[mid] < array[i])                    left = mid + 1;              else                     right = mid - 1;          }                    maxV[right+1] = array[i];           ret = std::max(right+1,ret);      }      return ret;}int main(){       int n;       vector<long long> vec;       while(scanf("%d",&n)!=EOF){             vec.clear();             vec.resize(n,0);             for(int i = 0; i < n; ++i){                   long long tmp;                   scanf("%lld",&tmp);                   vec[i] = tmp;             }             printf("%d\n",LongestIncreasingSequence(vec));      }      return 0;}


原创粉丝点击