●BZOJ 3831 [Poi2014]Little Bird

题链:

单调队列优化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
",F[N]);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/zj75211/p/8108747.html