NKOJ 小鸟 [DP][单调队列优化]

NKOJ 小鸟 [DP][单调队列优化]

问题描述

有一排n棵树,第i棵树的高度是Di。
一群小鸟要从第1棵树飞到第n棵树去玩。
不同小鸟的飞跃能力不同,第i只小鸟的飞跃能力为ki,表示如果当前它位于第x号树,那么它可以飞到x+1,x+2,……,x+ki号树上去,也就是一次可以飞过ki棵树。
如果小鸟飞到一棵不矮于当前树的树,那么他的劳累值会+1,否则不会。
小鸟们希望最小化劳累值,请你计算每只小鸟达到终点所需最小劳累值。

输入格式

第一行,一个整数N(2<=N<=100000)
第二行,N个空格间隔的整数,第i个数表示第i棵树的高度Di。(1<=Di<=10^9)
第三行,一个整数Q(1<=Q<=25),表示小鸟的数量
接下来Q行,每行一个整数,其中第i个整数表示第i只小鸟的飞跃能力ki。

输出格式

Q行,每行一个整数,表示对应小鸟的劳累值

解法

方程我就懒得写了…

主要是单调队列优化。最开始我想了想sparse table,但是显然这是行不通的。然后我就直接交了朴素DP,TLE没得说。

关键代码:

for(int i=2;i<=n;i++){
    //满足题目要求:一只鸟最多能飞行k棵树
    while(q.size() && i-q.front()>k)q.pop_front();
    f[i]=f[q.front()]+(D[i]>=D[q.front()]);
    //如果f[q.back()]>f[i],那么再加进f[i]就一定不单调
    //如果f[q.back()]==f[i],那么如果第i棵树比第q.back()棵树更高或等高,那么再从q.back()跳一定会增加疲劳值
    while(q.size() && (f[q.back()]>f[i] || (f[q.back()]==f[i] && D[q.back()]<=D[i])))q.pop_back();
    q.push_back(i);
}

代码

#include<iostream>
#include<cstdio>
#include<queue>
#define N 101000
using namespace std;
int D[N],f[N];
int main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&D[i]);
    int m;scanf("%d",&m);
    deque<int>q;
    for(int o=1;o<=m;o++){
        int k;scanf("%d",&k);
        while(!q.empty())q.pop_back();
        f[1]=0;q.push_back(1); 
        for(int i=2;i<=n;i++){
            while(q.size() && i-q.front()>k)q.pop_front();
            f[i]=f[q.front()]+(D[i]>=D[q.front()]);
            while(q.size() && (f[q.back()]>f[i] || (f[q.back()]==f[i] && D[q.back()]<=D[i])))q.pop_back();
            q.push_back(i);
        }
        printf("%d\n",f[n]);
    } 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/arliastark/article/details/80607896