最长递增子序列,传统方法用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;
}