[四连测(一)]猴子

题目描述

有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的思想,只不过写出来没这么正式罢了。

原文地址:https://www.cnblogs.com/lover-fucker/p/13566703.html