dp基础——最长上升子序列(lis)

LIS问题总共有两种解法。
方法一:时间复杂度O(n^2), 设dp[i]表示以第i个数字为结尾的最大长度。所以对于每一位i需要遍历[1,i-1],来寻找最优解。
需要注意的是如果有n位数字,dp[n]并不意味着是最优解,最优解需要遍历整个dp数组才能知道。这和这题有点像,相对于第二种方法,这种方法更具有普遍性。

#include <iostream>
#include  <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1005;
int n;
int dp[maxn];
int a[maxn];
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for(int i = 1; i <= n; i++) {
        dp[i] = 1;
        for(int j = 0; j < i; j++) {
            if(a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,dp[i]);
    }
    printf("%d\n",ans);
    return 0;
}

第二种方法的时间复杂度是O(nlogn)。我们将满足的元素放入一个栈中,如果a[i]比当前栈的顶层元素都要大,就将a[i]t添加到栈顶。反之如果是求最大严格上升子序列,就用lower_bound()寻找并将该位置替换,如果是非递减序列就用upper_bound()即可.

#include <iostream>
#include  <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1005;
int n;
int dp[maxn];
int a[maxn];
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    int len = 1;
    dp[1] = a[1];
    for(int i = 2; i <= n; i++) {
        if(a[i] > dp[len]) dp[++len] = a[i];
        else {
            int j =lower_bound(dp+1,dp+1+len,a[i])-dp;
            dp[j]=a[i];
        }
    }
    printf("%d\n", len);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/80365326