题目描述
有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并不复杂。
这个状态转移方程后面的比较就是判断从j到i是否+1,虽然看起来是一维的,但实现是需要的时间复杂度的,那么这道题怎么可能过得了。于是,我们便要考虑优化了。
我们看状态转移方程,其中的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的思想,只不过写出来没这么正式罢了。