最长上升子序列

两种复杂度的解法就不说了,直接贴代码(题目来源为:[POJ](http://poj.org/problem?id=2533)。

第一份代码没有自己写二分(打算后面会补一份自己写二分查找的),而是直接用了C++标准库的lower_bound函数,十分简洁!!!
不过我昨晚自己想的时候,没想明白为什么B数组是递增的?想要证明,发现智商不够,正面想不出怎么证明,只好用数学归纳法的想法,很容易就证出来了——
1)归纳基:首先初始时B数组只有一个值,肯定是有序的。
2)归纳步:每次查找x后B的长度会扩展,都是因为找到比当前B数组的最后一位的值还要大的去扩展,那么数值x肯定比B中所有值都大的。

结论显然了。
不过问题又来了,这样做的正确性???
正确性在于两个关键的地方:
1)同样的长度,贪心选择数值小的;
2)扩展长度(好吧,感觉这里我没说清楚,不过初学者最好不要看我的这篇博客╮(╯▽╰)╭会晕的)

我的解答:
二分查找的结果是什么意义呢?——假设它找到了B数组中的第k位,由于x比B[k-1]大,所以表示以x结尾的最长上升子序列的长度可以为(k-1)+1=k;但是,x又不大于B[k],所以以x结尾的最长上升子序列的长度不会超过k。所以,使用“夹逼定理”,目前来说,以x结尾的最长上升子序列的长度就应该是k。
剩下的贪心很容易理解了~相同的长度,肯定是选择数值小的,后面才有可能放更多的数值,这个也是可以很容易写书面证明的。

#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;

int LCS(vector<int>& v) {
    vector<int> B(v.size()+1, 0);
    int len = 1;
    B[0] = v[0];
    for (int i = 1; i < v.size(); ++i) {
        int pos = lower_bound(B.begin(), B.begin()+len, v[i]) - B.begin();
        B[pos] = v[i];
        len = max(len, pos+1);
    }
    return len;
}

int main() {
    int n, tmp;
    scanf("%d", &n);
    vector<int> v(n);
    for (int i = 0; i < n; ++i) {
        scanf("%d", &tmp);
        v[i] = tmp;
    }
    printf("%d\n", LCS(v));
    return 0;
}

你可能感兴趣的:(算法)