[HDU 5489]Removed Interval[LIS]

来源:互联网 发布:上帝软件wpe1.0 编辑:程序博客网 时间:2024/06/05 05:11
题目链接:[HDU 5489]Removed Interval[LIS]

题意分析:

求含有N个元素的数组,去掉L个连续的元素后,剩下元素构成的最长上升子序列的长度。

解题思路:

我们就可以枚举这个长度为L的区间,从左往右滑动窗口。每向右移动一格,此时有:

最长上升子序列长度 = 窗口右边以右边第一个元素开头的最长上升子序列 + 窗口左边最大元素小于窗口右边第一个元素的最长上升子序列。 

例如:

1 4 3 [5 7 4 9] 4 5 6 8

此时最长上升子序列长度 = 4(4,5,6,8) + 2(1,3) = 6。

如何求右边第一个元素开头的最长上升子序列呢?答案就是我们从右往左做LIS。

如何求左边呢?我们可以边移动窗口边做LIS,然后每次查询右边第一个元素在左边LIS中的位置,然后左边长度 = 位置 + 1。(因为我下标是从0开始XD)

个人感受:

原来我是想着先求出原序列的LIS,然后滑动窗口求它什么时候窗口里LIS内元素最小,结果GG。现在的是队友的解法,大赞~

具体代码如下:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int MAXN = 1e5 + 111;int a[MAXN], arr[MAXN], b[MAXN], dp[MAXN]; // arr:LIS  dp:以这个开头的最长上升子序列int main(){    int t, kase;    for (int kase = scanf("%d", &t); kase <= t; ++kase)    {        int n, l;        printf("Case #%d: ", kase);        scanf("%d%d", &n, &l);        for (int i = 0; i < n; ++i)        {            scanf("%d", &a[i]);            b[i] = -a[i]; // 取相反数,这样可以从后往前LIS        }        int x;        memset(arr, 0x7f, sizeof arr);        for (int i = n - 1; i >= l; --i)        {            x = lower_bound(arr, arr + n, b[i]) - arr;            arr[x] = b[i];            dp[i] = x + 1; // 长度等于下标 + 1        }        int ans = 0, mxlen = 0;        memset(arr, 0x7f, sizeof arr);        for (int i = l; i < n; ++i)        {            x = lower_bound(arr, arr + n, a[i]) - arr; //从左边的LIS查找            ans = max(ans, x + 1 + dp[i] - 1); // 之所以减一是因为我们从左边查找的是大于等于的            x = lower_bound(arr, arr + n, a[i - l]) - arr; // 左边更新LIS            arr[x] = a[i - l];            mxlen = max(mxlen, x + 1);        }        ans = max(ans, mxlen); //最后要比较下滑动窗口滑动到最后的情况        printf("%d\n", ans);    }    return 0;}


0 0