• LIS教学篇


    LIS是最长上升子序列,(递增子序列是指,子序列的元素是递增的)例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10。

    对于LIS,有两种解法,一种是比较容易想到的O(n^2)的解法,我们用N[i]表示第i个数,dp[i]表示第i个数所在子序列中最大的长度(比如对于序列{1,2,5,4,2},那dp={1,2,3,3,2}).那么我们只要枚举j从1到i-1,且N[j]<N[i],就可以进行更新:dp[i]=min(dp[i],dp[j]+1),这样的复杂度是O(n^2)。

    O(n^2)的复杂度往往是不如人意的,我们可以考虑尝试优化,如果我们用dp[i]表示长度为i的子序列的最后一个数的最小值,那么我们可以发现这样的dp数组一定是非递减的,因为如果长度为2的子序列末尾最小值是3,那长度为1的子序列末尾最小值肯定可以取3,是不可能小于3的,因此我们可以先把数组初始化为INF,每次更新的时候二分一下dp数组,找到一个大于等于N[i]的数的位置,然后在这个地方更新。举个例子,对于

    5 1 6 8 2 4 5 10这个序列

    一开始dp数组为:

    INF INF INF INF INF INF INF INF

    更新5的时候,在1的位置(下标从1开始)更新,表示长度为1的子序列的最后一个数最小是5,此时dp数组为

    5 INF INF INF INF INF INF INF

    更新1的时候,二分到1的位置,在1的位置更新,表示长度为1的子序列的最后一个数最小是1,此时dp数组为

    1 INF INF INF INF INF INF INF

    更新6的时候,二分到2的位置,在2的位置更新,表示长度为2的子序列的最后一个数最小是6,此时dp数组为

    1 6 INF INF INF INF INF INF

    更新8的时候,二分到3的位置,在3的位置更新,表示长度为3的子序列的最后一个数最小是8,此时dp数组为

    1 6 8 INF INF INF INF INF

    更新2的时候,二分到2的位置,在2的位置更新,表示长度为2的子序列的最后一个数最小是2,此时dp数组为

    1 2 8 INF INF INF INF INF

    更新4的时候,二分到3的位置,在3的位置更新,表示长度为3的子序列的最后一个数最小是4,此时dp数组为

    1 2 4 INF INF INF INF INF

    更新5的时候,二分到4的位置,在4的位置更新,表示长度为4的子序列的最后一个数最小是5,此时dp数组为

    1 2 4 5 INF INF INF INF

    更新10的时候,二分到5的位置,在5的位置更新,表示长度为5的子序列的最后一个数最小是10,此时dp数组为

    1 2 4 5 10 INF INF INF

    这时候dp数组处理完毕,5为LIS长度。

    可以在这里测试下代码:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1134

    代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #include <queue>
    #include <string>
    #include <stack>
    #include <map>
    #include <set>
    #include <bitset>
    #define X first
    #define Y second
    #define clr(u,v); memset(u,v,sizeof(u));
    #define in() freopen("data","r",stdin);
    #define out() freopen("ans","w",stdout);
    #define Clear(Q); while (!Q.empty()) Q.pop();
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int maxn = 1e5 + 10;
    const int INF = 0x3f3f3f3f;
    int c[maxn];//c[i]代表序列长度为i时的最小数字
    int main()
    {
        int n, x;
        scanf("%d", &n);
        clr(c, 0x3f);//一开始全部初始化为INF
        for (int i = 0; i < n; i++)
        {
            scanf("%d", &x);
            int pos = lower_bound(c + 1, c + 1 + n, x) - c;//找到c中第一个大于等于x的数字的下标
    //        for (int j = 1; j <= n; j++)
    //            printf("%d ", c[j]);
    //        puts("");
    //        printf("pos : %d
    ", pos);
            c[pos] = min(c[pos], x);
        }
        printf("%d
    ", lower_bound(c + 1, c + 1 + n, INF) - (c + 1));
        return 0;
    }
  • 相关阅读:
    手搓一个兔子问题(分享一个C语言问题,持续更新...)
    一个C语言萌新的学习之旅(持续更新中...)
    ...续上文(一个小萌新的C语言之旅)
    手搓一个C语言简单计算器。
    嘿,C语言(持续更新中...)
    一个博客萌新的博客之旅。。。。
    vue+uikit3+laravel快速建站
    mpvue开发博客园小程序
    C语言俄罗斯方块小游戏练习
    c语言贪吃蛇小游戏练习
  • 原文地址:https://www.cnblogs.com/scaugsh/p/6392331.html
Copyright © 2020-2023  润新知