• [四连测(一)]猴子


    题目描述

    有Q只猴子要从第一棵树到第n棵树去,第i只猴子一次跳跃的最远距离为Ki。如果它在第x棵树,那它最远可以跳到第x+Ki棵树。如果第j棵树的高度比第i棵树高或相等,那么它从第i棵树直接跳到第j棵树,它的劳累值会增加1。所有猴子一开始在第一棵树,请问每只猴子要跳到第n棵树花费的劳累值最小。

    输入

    第一行一个整数n,表示有n棵树。(2<=n<=1000000)

    接下来第二行给出n个正整数D1,D2,……,Dn(1<=Di<=10^9),其中Di表示第i棵树的高度。

    第三行给出了一个整数Q(1<=Q<=25),接下来Q行,给出了每只猴子一次跳跃的最远距离Ki(1<=Ki<=N-1)。

    输出

    输出Q行,每行一个整数,表示一只猴子的最小的劳累值。

    样例输入

    9
    4 6 3 6 3 7 2 6 5
    2
    2
    5

    样例输出

    2
    1

    解题思路

    可以很明显地看出来是DP题目。DP的思路还是比较好想,因为这一道题就是线性的,并不是说什么多维那种,所以这道题DP并不复杂。

    dp[i] = min(dp[j] + (h[i] >= h[j]))

    这个状态转移方程后面的比较就是判断从j到i是否+1,虽然看起来是一维的,但实现是需要O(n^2)的时间复杂度的,那么这道题怎么可能过得了。于是,我们便要考虑优化了。

    我们看状态转移方程,其中的j是有一个范围的,就是 i - k + 1 <= j <= i - 1。又是求最小值,我们便可以想到些什么东西

    的确,这道题需用单调队列来进行优化。维护队列中元素递增,并且判断队首元素是否在区间内。这样就得到了最小的dp[i]了。但怎么确定优先级呢。对于不同的高度还有加1和不加1的这一种情况呢。难道要将全部放进单调队列中吗?

    答案是否定的。我们姑且抛掉后面的比较。先讨论一下最小的dp[j]

    (一)如果dp[j]最小且唯一,就假设它加上1吧。还是最小的,肯定可以选啊。这种情况很好讨论。

    (二)如果有多个最小值,我们便需判断一下了,我们想想,说不定这些j所在的高度是有些需要加1,但有些是不需要加1的,那么,不需要加1的那一些高度肯定优于其他的那些高度。但我们又不确定是否不用加1,但我们不考虑这么多,在这些最小值找到一个最高的。最后判断就行了,如果它不用+1,那是最好的,如果它要+1,其他的也一样的。于是我们便讨论完了所有情况,可以用单调队列来做了

    代码如下:

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<vector>
    #include<cstdlib>
    #define M 1000005
    using namespace std;
    struct node {
        int x,id;
        inline node (){};
        inline node (int X,int ID){
            x = X;
            id = ID;
        }
    };
    int n,a[M],dp[M],q;
    int head = 1,tail;
    node Queue[M];
    inline int read(){
        int f = 1;
        int x = 0;
        char s = getchar();
        while (s < '0' || s > '9'){
            if (s == '-')
                f = -1;
            s = getchar();  
        }
        while (s >= '0' && s <= '9'){
            x = x * 10 + s - '0';
            s = getchar();
        }
        return x * f;
    }
    int main(){
        n = read();
        for (int i = 1;i <= n; i ++ ){
            a[i] = read();
        }
        q = read();
        for (int i = 1 ;i <= q ;i ++ ){
            head = 1, tail = 1;
            Queue[tail] = node (dp[1],1);
            int x = read();
            for (int j = 2;j <= n; j ++ ){
                while (Queue[head].id < j - x ){
                    head ++ ;
                }
                if (a[Queue[head].id] > a[j]){
                    dp[j] = dp[Queue[head].id];
                }
                else
                    dp[j] = dp[Queue[head].id ] + 1;
                while (head <= tail ){
                    node  L = Queue[tail];
                    if (L.x < dp[j]){
                        Queue[++ tail] = node (dp[j], j);
                        break;
                    }
                    else if (L.x == dp[j] && a[Queue[tail].id] > a[j]){//这一个地方太重要了,考试时候没写卡了我60分。
                        Queue[++ tail] = node (dp[j], j);
                        break;
                    }
                    else
                        tail -- ;
                }
                if (head > tail){
                    Queue[++ tail] = node(dp[j],j);
                }
            }
            printf("%d
    ",dp[n]);
        }
    }

    总结

    可以说就是考试的时候没考考虑完全,导致这道题一半的分都没有,其实思路大部分是对的。其实关于有多个最小值选哪个我是在脑中闪过这一个念头的,当时的我自信地认为这一种情况应该是不用讨论的。现在觉得当时就应该多想想。整道题的难度呢,我是觉得不大的。甚至可以说是模板,有些细节需要注意罢。

    值得一提的是:此题似乎可以用贪心来做。就不用这一个单调队列来优化DP了,不知道是数据的原因还是题目的特性,但DP可以说是没有问题的,说不定所谓的贪心算法也是运用了DP的思想,只不过写出来没这么正式罢了。

  • 相关阅读:
    【Nginx学习】安装及常用命令
    【Nginx学习】基础知识
    【Nginx学习】Xshell7连接CentOS7艰难轶事
    【LeetCode刷题】5343. 多次求和构造目标数组:妙解
    【LeetCode刷题】1353. 最多可以参加的会议数目
    【转载】priority_queue用法
    【LeetCode刷题】供暖器:妙解
    【妙解】重复的子字符串
    【转载学习】基金理财学习
    【转载】sync_with_stdio + cin.tie
  • 原文地址:https://www.cnblogs.com/lover-fucker/p/13566703.html
Copyright © 2020-2023  润新知