序列变换 HDU - 5256

Posted ljxtt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了序列变换 HDU - 5256相关的知识,希望对你有一定的参考价值。

序列变换 HDU - 5256

题目链接

题目

我们有一个数列A1,A2...An,你现在要求修改数量最少的元素,使得这个数列严格递增。其中无论是修改前还是修改后,每个元素都必须是整数。
请输出最少需要修改多少个元素。

input

第一行输入一个T(1≤T≤10),表示有多少组数据

每一组数据:

第一行输入一个N(1≤N≤105),表示数列的长度

第二行输入N个数A1,A2,...,An。

每一个数列中的元素都是正整数而且不超过106。

output

对于每组数据,先输出一行

Case #i:

然后输出最少需要修改多少个元素。

Sample Input

2
2
1 10
3
2 5 4

Sample Output

Case #1:
0
Case #2:
1

分析

一开始很容易就想到 (mathrm{LIS}) 。先算出 (mathrm{LIS}) 的长度,然后序列总长度减去 (mathrm{LIS}) 的长度就行了。

但是隐隐间觉得有点不对,留意到题目中的严格递增,就想到了如果连续几个都是相同的怎么办。然后就举出了反例。

例如:1 2 2 2 3 。这个序列的 (mathrm{LIS}) 为 1 2 3。按照上面的的方法算出的答案是 2 。但实际上,我们要修改成 1 2 3 4 5。要修改的个数为 3。

但我们还是想要往 (mathrm{LIS}) 上靠,仔细分析一下,我们修改后的整个数组要满足如下的条件:
[ arr[i + 1] ge arr[i] + 1 ]
怎么往 (mathrm{LIS}) 上靠呢?(mathrm{LIS}) 最后的式子是不是要满足:
[ a[i + 1] ge a[i] ]
第一个式子中的 (1) 是不是有点碍事,留意到两边其实可以做一个操作:
[ arr[i + 1] - i - 1 ge arr[i] - i ]
两边都可以减去一个 (i) , 那么就可以转化到 (mathrm{LIS}) 的做法了。

再留意到 (n le 10^5,T le 10)(O(n^2)) 的复杂度肯定是接受不了,要对朴素的做法进行一些优化。

其实也是一个贪心的做法。我们可以去维护这样一个 (dp) 数组。不妨设 (len)(dp) 数组的长度。在遍历数组 (arr) 的时候,有两种情况:

  1. (arr[i] ge dp[len - 1]),把 (arr[i]) 加到 (dp) 数组的结尾。
  2. (arr[i] < dp[len - 1]),二分查找 (dp) 数组里,第一个大于 (arr[i]) 数字,然后替换成 (arr[i])

然后,最后 (len) 就是 (mathrm{LIS}) 的长度。

看得出来,数组 (dp) 本身就在维护一个尽可能长的上升序列,第一个操作很好理解,那么对于第二个操作,由贪心我们可以知道,如果有几个序列,那么序列末尾的数字越小,那么它能成为 (mathrm{LIS}) 的概率越大?所以第二个操作就是不断地在保证它在成为 (mathrm{LIS})。如果但是如果替换的不是最后一个呢?由于是替换,不是增加或者减小,所以这对于最后的结果并没有影响,所以替换前面的数字并没有影响。(这造成了 (dp) 数组并不是 (mathrm{LIS}) )。

所以用二分就能把 (mathrm{LIS}) 优化到 (O(n log n))

代码

#include <bits/stdc++.h>
#define lowbit(x) ((x)&(-x))
#define mem(i, a) memset(i, a, sizeof(i))
#define sqr(x) ((x)*(x))
typedef long long ll;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 7;
using namespace std;
int arr[maxn], dp[maxn];
int main(void){
#ifdef ljxtt
freopen("data.in", "r", stdin);
#endif
    int T; scanf("%d", &T);
    int cases = 1;
    while(T--){
        int n; scanf("%d", &n);
        for(int i = 0; i < n; i++){
            scanf("%d", &arr[i]);
            arr[i] -= i;
        }
        int len = 0;
        dp[0] = arr[0];
        for(int i = 1; i < n; i++){
            if(arr[i] >= dp[len])
                dp[++len] = arr[i];
            else
                *upper_bound(dp, dp + len + 1, arr[i]) = arr[i];
        }
        printf("Case #%d:
%d
", cases++, n - len - 1);
    }
    return 0;
}

以上是关于序列变换 HDU - 5256的主要内容,如果未能解决你的问题,请参考以下文章

序列变换(HDU-5256)LIS

序列变换

hdu 5256

hdu No.5248 序列变换(二分+贪心)

hdu No.5248 序列变换(二分+贪心)

hdu 5248 序列变换