假期(holiday)
【问题描述】
OIER们经过了几个月努力的学习,教练决定让OIER们放假。假期可以在1…N天内任意选择一段(需要连续),每一天都有一个享受指数W。但是OIER们的要求非常苛刻,假期不能短于P天,否则OIER们不能得到足够的休息;假期也不能超过Q天,否则OIER就会有荒废学业的危险。我们想知道OIER们能获得的最大享受指数。
【输入格式】
从文件holiday.in中输入数据。
第一行:N,P,Q.
第二行:N个数字,中间用一个空格隔开。
【输出格式】
输出到文件holiday.out中。
一个整数,OIER们能获得的最大享受指数。
【样例输入】
5 2 4
-9 -4 -3 8 -6
【样例输出】
5
【数据规模与约定】
对于 50%的数据,1=<N<=10000
对于 100%的数据,1=<N<=100000
数据有梯度
题解:这题是个SB题啊QAQ,然而我只知道单调队列是个什么东西,并不知道他能干什么QAQ,单调队列在求滑动窗口最大最小值的时候可发挥重大作用。
例如这样:给出一个队列,每次可以在队尾加入数字,或者在队首取出数字,每次操作后求出队列中当前所有数字的最大值.对于两个数字a,b,如果a<b,而且a在b之前加入,那么a永远不可能成为最优解.如果a<b,但是a在b之后加入,那么a可能成为最优解.我们维护”可能成为最优解的数字集合”,一个数字可能成为最优解,当且仅当现在右侧不存在比它大的数字.(假设左端是队首,右端是队尾)
我们要做的是当插入一个数字或删除一个数字时,快速更新”可能成为最优解的数字集合” 我们另外开一个队列,在里面按下标从左到右的顺序存储”可能成为最优解的数字集合”,它们都满足当前队列右侧没有比自己大的数字.这些可能的最优解的数值大小必然从左向右递减.
处理插入
新插入的数字一定可以成为最优解,因为它的右边没有数字
原先不能成为最优解的位置在新插入一个数字之后还是不能成为最优解
原先能成为最优解的位置在新插入一个数字之后有可能不能成为最优解.如果新插入的数字比这个位置的数字大,这个位置就不再能够成为最优解.
处理删除
删掉的位置不在”可能的最优解”中,无影响
删掉的位置是可能的最优解,这个位置一定是当前可能的最优解中最靠左的,直接删掉即可
处理查询
直接输出当前保存的”可能的最优解”中最靠左的(也就是队首的,因为我们用另一个队列存储这些可能位置)
那么如何使用单调队列优化dp,因为动态规划的某些方程具有某些“单调性”,或者可以通过化简变成“滑动窗口最大/最小值”的形式
就如这道题,选出一个长度在【l,r】之间的区间,使得数字之和最大,
先做一次前缀和,枚举右端点,使左端点减去的前缀和尽可能的小,所以是滑动窗口最小值,
那么这道题的代码就砰的一下出来了QAQ:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define inf 1ll<<62
using namespace std;
typedef long long ll;
int n,p,q;
ll sum[N];
ll f[N];
int que[N];
int main()
{
scanf("%d%d%d",&n,&p,&q);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
sum[i]=sum[i-1]+x;
}
int head=1,tail=0;
ll ans=-inf;
for(int i=p;i<=n;i++)
{
while(head<=tail&&sum[que[tail]]>=sum[i-p])
tail--;
que[++tail]=i-p;
while(que[head]<i-q)
head++;
f[i]=sum[i]-sum[que[head]];
ans=max(ans,f[i]);
}
printf("%lld\n",ans);
return 0;
}