最长递增子序列(Longest increasing subsequence)

问题定义:

    给定一个长度为N的数组A,找出一个最长的单调递增子序列(不要求连续)。

 

    这道题共3种解法。

1. 动态规划

    动态规划的核心是状态的定义和状态转移方程。定义lis(i),表示前i个数中以A[i]结尾的最长递增子序列的长度。可以得到以下的状态转移方程:

d(i) = max(1, d(j) + 1), 其中j < i,且A[j] <= A[i]

程序实现:

int longestIncreasingSubsequence(vector<int> nums)
{
    if (nums.empty())
        return 0;
    int len = nums.size();
    vector<int> lis(len, 1);    

    for (int i = 1; i < len; ++i)
    {
        for (int j = 0; j < i; ++j)
        {
            if (nums[j] < nums[i] && lis[i] < lis[j] + 1)
                lis[i] = lis[j] + 1;
        }
    }
    return *max_element(lis.begin(), lis.end());
}

程序复杂度为O(N^2)

2. 动态规划 + 二分查找

    换一种角度看问题。令Ai,j表示所有长度为j的最大递增子序列的最小末尾,我们有Ai,1 < Ai,2 < ... < Ai,j。

对A[i+1]来说,有两种选择。

1. Ai,j < A[i+1], 此时我们可以得到一个长度为i+1的最大递增子序列。

2. 替换Ai,k,如果Ai,k-1 < A[i+1] < Ai,k。

替换长度为k的最大递增子序列的最小末尾,是为了增大获取更长子序列的机会。

程序实现:

int binarySearch(const int arr[], int low, int high, int val)
{
    while (low <= high)
    {
        int mid = low + (high - low) / 2;    // Do not use (low + high) / 2 which might encounter overflow issue

        if (val < arr[mid])
            high = mid - 1;
        else if (val > arr[mid])
            low = mid + 1;
        else
            return mid;
    }
    return low;
}
int LIS(int arr[], int n)
{
    int *minTail = new int[n];
    minTail[0] = arr[0];
    int len = 1;
    for (int i = 1; i < n; ++i)
    {
        if (arr[i] > minTail[len-1])
            minTail[len++] = arr[i];
        else
        {
            int pos = binarySearch(minTail, 0, len-1, arr[i]);
            minTail[pos] = arr[i];
        }
    }
    delete [] minTail;
    return len;
}

复杂度:O(nlogn)

reference : 

最长递增子序列(LIS)

3. 排序+LCS

    这种方法就不细说了。。。

posted @ 2015-08-12 16:34  Sawyer Ford  阅读(283)  评论(0编辑  收藏  举报