关于最长上升子序列的问题(LIS)

一般在做这种题目会有两种算法,一个是复杂度是O(n ^ 2),另一种就是O(nlogn)。



O(n ^ 2)算法:

d(i)为以i结尾的最长上升子序列的长度,则d(i) = max{0, d(j) | j < i, Ai > Aj} + 1;最后答案为max{d(i)};

//最长上升子序列
//O(n * n)
int LIS() {
    int Max = 1;
    memset(dp, 0, sizeof(dp));
    for (int i = 0; i < n; i++) {
        dp[i] = 1; 
        for (int j = 0; j < i; j++)
            if (a[i] > a[j])
                dp[i] = max(dp[i], dp[j] + 1); 
        if (Max < dp[i])
            Max = dp[i];
    }
    return Max;
}



O(nlogn)算法:

假设已经计算出来的两个状态a和b满足Aa < Ab且d(a) = d(b),则对于后续所有状态i(即i > a 且 i > b)来说,a一定不会比b差——因为如果b所能满足的Ab < Ai的条件,a也能满足且,d(a) = d(b);但相反的a所能满足的b不一定能够满足。所以我们一直保留更小的a时,就一定能保证最后的值是最优解。

而要找到当d值相同但更小的那个数,那就要用到二分查找,来降低时间复杂度了。

//O(N*log(N))
//二分搜索降低时间复杂度(手写二分查找)
int search(int n, int num) {
    int l = 1, r = n; 
    while (l <= r) {
        int mid = (l + r) / 2; 
        if (num > c[mid] && num <= c[mid + 1]) 
            return mid + 1;
        else if (num <= c[mid])
            r = mid - 1;
        else 
            l = mid + 1; 
    } 
}

void LIS() {
    int size = 1, temp, Max = 0; 
    c[1] = a[1];
    dp[1] = 1;
    for (int i = 2; i <= n; i++) {
        if (a[i] <= c[1]) 
            temp = 1;
        else if (a[i] > c[size])
            temp = ++size;
        else
            temp = search(size, a[i]); 
        c[temp] = a[i]; 
        dp[i] = temp;
        if (dp[i] > Max)
            Max = d[i];
    } 
}


//O(N*log(N))
//二分搜索降低时间复杂度(函数二分查找)
void LIS() {
    for (int i = 1; i <= n; i++)
        g[i] = INF;
    for (int i = 0; i < n; i++) {
        int k = lower_bound(g + 1, g + n + 1, arr[i]) - g; 
        d[i] = k; 
        g[k] = arr[i]; 
        ans = max(ans, d[i]);
    } 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值