最长上升子序列(LIS)

LIS定义:

        最长上升子序列(Longest Increasing Subsequence,LIS),在计算机科学上是指一个序列中最长的单调递增的子序列。

问题描述:

        这类问题一般表述为:给一个数列,长度为n,求LIS长度。

样例输入:

        循环每两行输入测试样例,第一行输入n,第二行输入n个数字

9
5 1 3 6 8 2 9 0 10
5
1 2 4 0 2
9
2 7 1 5 6 4 3 8 9

样例输出:

        每个测试样例,输出一行LIS长度

6
3
5

 O(n^2)方法:

        使用动态规划思想,d[i]表示前i个的解,那么就有状态转移方程 d[i] = max{ 1, d[j] + 1 }, j < i, a[j] < a[i].

#include <cstdio>
#include <algorithm>
using namespace std;

const int N = int(2e5 + 5);

// 求LIS的长度O(n^2)
int g_d[N];
int LIS(const int arr[], const int &len)
{
    int i, j, ans = 0;
    for (i = 0; i < len; i++)
    {
        g_d[i] = 1;
        for (j = 0; j < i; j++)
        {
            if (arr[j] < arr[i]) g_d[i] = max(g_d[i], g_d[j] + 1);
        }
        ans = max(ans, g_d[i]);
    }
    return ans;
}

int main()
{
    int n, i;
    int a[N];
    while (scanf("%d", &n) != EOF)
    {
        for (i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
        printf("%d\n", LIS(a, n));
    }
    return 0;
}

O(nlogn)方法:

        在O(n^2)方法的第二重循环上使用单调数组维护:如果a[i] > d[len - 1],d[len++] = a[i]; 如果a[i] < d[len - 1],则利用二分查找前len个元素,找到第一个大于等于a[i]的位置k,将其替换d[k] = a[i](如果k后面还有元素,不需要动,这样的巧妙之处就是当a[i + 1]再与d[]末尾元素比较的时候仍是前i个数字的最优答案的最大值,这里的最优答案便是LIS长度不变的情况下,每个元素的值尽量小)

#include <cstdio>
#include <algorithm>
using namespace std;

const int N = int(2e5 + 5);

// 求LIS的长度O(nlogn)
int g_d[N];
int LIS(const int arr[], const int &len)
{
    int i, j, k = 0;
    g_d[k++] = arr[0];
    for (i = 1; i < len; i++)
    {
        if (arr[i] > g_d[k - 1]) g_d[k++] = arr[i];
        else g_d[lower_bound(g_d, g_d + k, arr[i]) - g_d] = arr[i];
    }
    return k;
}

int main()
{
    int n, i;
    int a[N];
    while (scanf("%d", &n) != EOF)
    {
        for (i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
        printf("%d\n", LIS(a, n));
    }
    return 0;
}

O(n^2) 求解LIS长度,并输出LIS串:

 

#include <cstdio>
#include <algorithm>
using namespace std;

const int N = int(2e5 + 5);

// 求LIS的长度O(n^2), 并输出LIS串
int g_d[N];
int g_pre[N];
int LIS(const int arr[], const int &len, int lisArr[])
{
    int i, j, ans = 0, maxId = 0;
    for (i = 0; i < len; i++)
    {
        g_pre[i] = i;
    }
    for (i = 0; i < len; i++)
    {
        g_d[i] = 1;
        for (j = 0; j < i; j++)
        {
            if (arr[j] < arr[i] && g_d[i] < g_d[j] + 1)
            {
                g_d[i] = g_d[j] + 1;
                g_pre[i] = j;
            }
        }
        if (ans < g_d[i])
        {
            ans = g_d[i];
            maxId = i;
        }
    }
    j = maxId;
    for (i = ans - 1; i >= 0; i--)
    {
        lisArr[i] = arr[j];
        j = g_pre[j];
    }
    return ans;
}

int main()
{
    int n, i;
    int a[N], lis[N];
    while (scanf("%d", &n) != EOF)
    {
        for (i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
        int ans = LIS(a, n, lis);
        printf("%d\n", ans);
        for (i = 0; i < ans; i++)
        {
            printf("%d ", lis[i]);
        }
        printf("\n");
    }
    return 0;
}

 O(nlogn)方法求解LIS串,在mask[]中用0标记出来,代码是当时做题的时候写的,没有优化,仅供参考。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = int(2e5 + 5);
const int MAX = 1 << 30;

// 求LIS的长度O(nlogn), LIS元素标记为1
int g_d[N];
int g_pre[N];

typedef struct _Node
{
    int v, idx;
} Node;
Node g_stk[N];
int g_top;

bool cmp(const Node &a, const Node &b)
{
    return a.v < b.v;
}

int LIS(const int arr[], const int &len, int mask[])
{
    g_top = -1;
    Node temp;
    int i, j, ans = 0, maxId = 0;
    g_d[0] = 1;
    ans = g_d[0];
    maxId = 0;
    g_stk[++g_top].v = arr[0];
    g_stk[g_top].idx = 0;
    for (i = 1; i < len; i++)
    {
        if (arr[i] > g_stk[g_top].v)
        {
            g_stk[++g_top].v = arr[i];
            g_stk[g_top].idx = i;
            j = g_top;
        }
        else 
        {
            temp.v = arr[i];
            temp.idx = i;
            j = lower_bound(g_stk, g_stk + g_top + 1, temp, cmp) - g_stk;
            g_stk[j] = temp;
        }
        if (j - 1 >= 0)
        {
            j = g_stk[j - 1].idx;
            g_d[i] = g_d[j] + 1;
            g_pre[i] = j;
        }
        else 
        {
            g_d[i] = 1;
        }
        if (ans < g_d[i])
        {
            ans = g_d[i];
            maxId = i;
        }
        // for (j = 0; j <= g_top; j++)
        // {
        //     printf("-%d", g_stk[j]);
        // }
        // printf("\n");
        // for (j = 0; j <= i; j++)
        // {
        //     printf("(d[%d]=%d),", j, g_d[j]);
        // }
        // printf("\n");
    }
    j = maxId;
    for (i = ans - 1; i >= 0; i--)
    {
        mask[j] = 1;
        j = g_pre[j];
    }
    return ans;
}

int main()
{
    int n, i;
    int a[N], mask[N];
    while (scanf("%d", &n) != EOF)
    {
        memset(mask, 0, sizeof(mask));
        for (i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
        }
        int ans = LIS(a, n, mask);

        // printf("%d\n", ans);
        // for (i = 0; i < n; i++)
        // {
        //     printf("%d", mask[i]);
        // }
        // printf("\n");

        int maxv = MAX;
        for (i = 0; i < n; i++)
        {
            if (!mask[i])
            {
                if (a[i] < maxv) maxv = a[i];
                else break;
            }
        }
        if (i < n) printf("NO\n");
        else
        {
            printf("YES\n");
            for (i = 0; i < n; i++)
            {
                printf("%d ", !mask[i]);
            }
            printf("\n");
        }
    }
    // printf("Time used %.2lf ms\n", 1000 * (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值