最长上升子序列nlog解法(带个人【详细讲解】)

最长上升子序列可以说是一道动态规划(n^2解法)的经典题目了吧,很早前就A掉了,也没有再研究,最近才发型最长上升子序列问题竟然有nlogn解法。

仔细研究了一下,发现解法中的一个思维的确非常巧妙,今天在此详细讲解一下,简单说一下我的理解。

先上代码,先简单看一下程序就行,讲解在后面

#include<stdio.h>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 1010;
int a[MAXN], b[MAXN];

//用二分查找的方法找到一个位置,使得num>b[i-1] 并且num<b[i],并用num代替b[i]
int Search(int num, int low, int high) {
    int mid;
    while(low <= high) {
        mid = (low + high) / 2;
        if(num >= b[mid])
            low = mid + 1;
        else
            high = mid - 1;
    }
    return low;
}

int DP(int n) {
    int i, len, pos;
    b[1] = a[1];
    len = 1;
    for(i = 2; i <= n; i++) {
        if(a[i] >= b[len]) { //如果a[i]比b[]数组中最大还大直接插入到后面即可
            len = len + 1;
            b[len] = a[i];
        } else { //用二分的方法在b[]数组中找出第一个比a[i]大的位置并且让a[i]替代这个位置
            pos = Search(a[i], 1, len);
            b[pos] = a[i];
        }
    }
    return len;
}

int main()
{
    int T, N;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&N);
        for(int i = 1; i <= N; i++){
            scanf("%d", &a[i]);
        }
        printf("%d\n", DP(N));
    if(T)
        printf("\n");
    }
    return 0;
}

我先来举个例子吧,输入

1 5

4 7 2 8 9

其中a为输入数组,b为答案数组(但有一点区别)

第一步:7 > 4,b添一个7

第二步:(比较巧妙了)2小于7,用二分的方法在b[]数组中找出第一个比2大的位置并且让2替代这个位置,结果b成了2,7(可以看出并不是子序列,稍后详细说明)

第三步:8 > 7,b添一个8

第四步:9 > 8,b添一个9,答案为4

可以发现此时b为2,7,8,9,乍一看根本就不是子序列,应该是4,7,8,9才对。

但其实第二步就巧在这个地方,比方如果之后碰到了2,3这种更友解的情况可以直接更迭4,但如果没有其实2也就指代了4。这样一种指代的思想非常巧妙。

还有不解的朋友我再提供两组数(自己凑的特殊数)

2

8

4 7 2 3 4 5 6 9

1 7 5 3 6 4 8 9 7

例题

https://nanti.jisuanke.com/t/17319

声明一下:版子来源kuangbin

猜你喜欢

转载自blog.csdn.net/a1097304791/article/details/82286906