zoukankan      html  css  js  c++  java
  • 序列变换 HDU

    序列变换 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;
    }
    
  • 相关阅读:
    移动端如何强制页面横屏
    css实现内容渐变隐藏效果,手机网页版知乎内容隐藏效果的实现
    css3中样式计算属性calc()的使用和总结
    如何使用JS操纵伪元素
    HTML5全局属性汇总
    20 个 CSS高级样式技巧汇总
    网页开发中利用CSS以图换字的多中实现方法总汇
    html/css解决inline-block内联元素间隙的多种方法总汇
    [算法] 泊松分布、指数分布
    [算法] 递归
  • 原文地址:https://www.cnblogs.com/ljxtt/p/12222906.html
Copyright © 2011-2022 走看看