题链:
单调队列优化DP
定义 F[i] 为到达第i课树的疲劳值。
显然最暴力的转移就是枚举i位置前面的k个位置,找到最优的位置跳过来。
每一个询问的复杂度 O(KN),最坏达到O(N²)。
考虑优化:
设H为高度的话,对于两个转移来源位置x,y(x<y),如何如何判断那个更优呢?
有如下4种情况:
1).H[y]≥H[x]且F[y]≤F[x],则任何时候选择y都不会比选择x差,所以从单调队列里去掉x。
2).H[y]≥H[x]且F[y]>F[x],在 x 由于距离原因而出队之前,选择x都不会比选择y差,所以把y放在队尾。
3).H[y]<H[x]且F[y]<F[x],则任何时候选择y都不会比选择x差,所以从单调队列里去掉x。
4).H[y]<H[x]且F[y]≥F[x],在 x 由于距离原因而出队之前,选择x都不会比选择y差,所以把y放在队尾。
然后用单调队列维护就可以啦,每一个询问的复杂度O(N)。
代码:
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 1000500 using namespace std; int H[MAXN],F[MAXN]; int N,Q; void read(int &x){ static int f; static char ch; x=0; f=1; ch=getchar(); while(ch<‘0‘||‘9‘<ch){if(ch==‘-‘)f=-1;ch=getchar();} while(‘0‘<=ch&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();} x=x*f; } bool off(int x,int y){ if(H[y]>=H[x]){ if(F[y]<=F[x]) return 1; else return 0; } else{ if(F[y]<F[x]) return 1; else return 0; } } int main(){ static int pos[MAXN],l,r; read(N); for(int i=1;i<=N;i++) read(H[i]); read(Q); for(int q=1,k;q<=Q;q++){ read(k); l=1; r=0; F[1]=0; pos[++r]=1; for(int i=2;i<=N;i++){ while(l<=r&&i-pos[l]>k) l++; F[i]=F[pos[l]]+(H[pos[l]]<=H[i]); while(l<=r&&off(pos[r],i)) r--; pos[++r]=i; } printf("%d\n",F[N]); } return 0; }