题目大意
跳跳虎喜欢在树林间跳跃,它从一棵较高的树跳到一棵较矮的树不需要消耗体力,否则消耗一点体力。
给出一排树,在一轮游戏的开始,跳跳虎在第 1 1 1棵树,它要跳到第 n n n棵树。每轮游戏有一个步伐限制 ,跳跳虎只能从第 i i i棵树跳到第 j ( i < j < = i + x ) j(i<j<=i+x) j(i<j<=i+x)棵树 ,求每轮游戏最少耗费多少体力。
思路
显然单调队列优化dp。
设
f
i
f_i
fi为前i个耗费最小体力值。
有
f
i
=
m
i
n
(
f
j
+
是
否
需
要
耗
费
体
力
)
(
m
a
x
(
0
,
i
−
x
)
<
=
j
<
i
)
f_i=min(f_j+是否需要耗费体力)(max(0,i-x)<=j<i)
fi=min(fj+是否需要耗费体力)(max(0,i−x)<=j<i)
剩下就是单调队列优化min过程,判断是否需要耗费体力是这样的:
若当前队首的体力耗费严格>现在的耗费,则队首出队。
若当前队首的体力耗费=现在的耗费但现在的树比队首的高,则队首出队。
现在的位置入队。
在处理某一位时,先把无效值出队,然后取队首的耗费,若队首比现在矮,再++。
实现见代码。
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<deque>
using namespace std;
int n,a[1000006],f[1000006],m,x;
deque<int> o;
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&m);
while (m--)
{
scanf("%d",&x);
o.clear();
memset(f,127,sizeof(f));
f[1]=0;
o.push_back(1);
for (int i=2;i<=n;i++)
{
while (o.size()&&i-o.front()>x) o.pop_front();
if (a[o.front()]>a[i]) f[i]=f[o.front()];
else f[i]=f[o.front()]+1;
while (o.size()&&(f[o.back()]>f[i]||f[o.back()]==f[i]&&a[o.back()]<=a[i])) o.pop_back();
o.push_back(i);
}
cout<<f[n]<<endl;
}
return 0;
}