题意:从1开始,跳到比当前矮的不消耗体力,否则消耗一点体力,每次询问有一个步伐限制,求每次最少耗费多少体力。
显然这道题可以用贪心做,而且多数情况时间复杂度比用单调队列优化DP低得多(当然DP也自有它的好处,比如不容易被卡常)。
我们用d表示树的编号,假设小鸟现在在d[i]的树,在所有可飞跃的树中(d[i+1]~d[i+k]),按以下策略选择:
优先考虑d[j]<d[i]的d[j],选择最大的d[j],如果有多个d[j]最大,选择最大的j。
如果没有d[j]<d[i]的d[j],只能考虑d[j]>=d[i]的d[j],同样选择最大的d[j],如果有多个d[j]最大,选择最大的j。
贪心的正确性显然。
代码实现:
#include <cstdio>
const int MAXN = 1000000;
//快读
inline int read() {
int x = 0, f = 1;
char s;
while((s = getchar()) < '0' || s > '9')
if(s == '-')
f = -1;
while(s >= '0' && s <= '9')
x = (x << 3) + (x << 1) + (s ^ '0'), s = getchar();
return x * f;
}
//快输
inline void write(int x) {
if(x < 0)
x = -x, putchar('-');
if(x / 10)
write(x / 10);
putchar(x % 10 + '0');
}
int n, q;
int d[MAXN + 5], k[MAXN + 5];
inline int answer(const int x) { //这里的x代表k[i]
int ans = 0;
int i = 1; //小鸟一开始的位置为1
while(true) {
if(i + x >= n) { //如果当前小鸟的位置可以直接飞到终点,由于不可能有比直接飞到终点的情况更优,所以直接飞到终点
if(d[n] >= d[i]) //如果终点大于小鸟的位置,答案就加1
++ans;
break;
}
bool flag = true; //表示能否找到小于d[i]的d[j]
int maxn = 0, numn;
for(int j = i + x; j > i; j--) {
if(d[j] < d[i]) {
flag = false; //找到就把flag置为false
if(d[j] > maxn) //倒序找则不能取等,因为要求最大的j
maxn = d[j], numn = j; //找到最大最远的小于d[i]的d[j]
}
}
if(flag) { //如果没有d[j]小于d[i]
int Max = 0, num;
for(int j = i + x; j > i; j--)
if(d[j] > Max)
Max = d[j], num = j; //找到最大最远的d[j]
++ans; //没有d[j]小于d[i],则答案必须加1
i = num; //将小鸟的位置变为符合策略的j
} else {
i = numn; //同上
}
}
return ans;
}
int main() {
n = read();
for(int i = 1; i <= n; i++)
d[i] = read();
q = read();
for(int i = 1; i <= q; i++)
k[i] = read(); //不喜欢边输入边输出,所以用数组存
for(int i = 1; i <= q; i++) {
int ans = answer(k[i]);
write(ans), putchar('\n'); //输出答案
}
}