[题解]琪露诺 单调队列优化dp
坑点挺多的。。。
首先推出一个基本的式子:
d
p
[
i
]
=
m
a
x
j
=
i
−
r
,
j
>
=
0
i
−
l
d
p
[
j
]
+
a
[
i
]
dp[i] = max_{j = i-r, j >= 0}^{i-l}dp[j] + a[i]
dp[i]=maxj=i−r,j>=0i−ldp[j]+a[i]
初始条件
d
p
[
0
]
=
0
dp[0]=0
dp[0]=0
这个式子成立的条件是,dp[j]能够到达。
然后就把所有
d
p
[
i
]
dp[i]
dp[i]赋值-inf,表示还没有到达。
我们用一个单调队列来维护区间
[
i
−
r
,
i
−
l
]
[i-r,i-l]
[i−r,i−l]的最大值,注意维护的时候,判断队首出队条件是
q
[
h
h
]
<
i
−
r
q[hh]<i-r
q[hh]<i−r,入队的元素是
i
−
l
i-l
i−l。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#define ll long long
#define pii pair<int,int>
using namespace std;
const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 2e5 + 10;
/*
dp[i] = max(dp[i-j]) + a[i];(l<=j<=r, i - j >= 0)
dp[0] = 0;
用单调队列维护区间[i-r,i-l]的最大值。
这个队列单调不增。
*/
int n,l,r;
int a[maxn];
int dp[maxn];
int q[maxn],hh,tt;
void solve(){
scanf("%d%d%d",&n,&l,&r);
for(int i = 0; i <= n; i++) scanf("%d",&a[i]);
for(int i = 0; i <= n; i++) dp[i] = -0x7fffffff;//表示所有点都还没有到过
dp[0] = 0;
//初始条件
hh = 1, tt = 0;
/*for(int i = 1; i <= n; i++){
if(hh <= tt && q[hh] < i - r) hh++;
while(hh <= tt && dp[q[tt]] < dp[i]) tt--;
dp[i] = dp[q[hh]] + a[i];
q[++tt] = i;
}*/
//这种写法是错误的,因为q维护的是1~i的最大值,而我们应该维护i-r~i-l的最大值。
for(int i = l; i <= n; i++){
if(hh <= tt && q[hh] < i - r) hh++;
while(hh <= tt && i - l >= 0 && dp[q[tt]] < dp[i-l]) tt--;
if(i - l >= 0) q[++tt] = i - l;
if(q[hh] != -0x7fffffff)dp[i] = dp[q[hh]] + a[i];
//有些点之前到不了,现在也到不了
}
int ans = -0x7fffffff;
for(int i = n - r + 1; i <= n; i++) ans = max(ans,dp[i]);
printf("%d\n",ans);
}
int main()
{
solve();
return 0;
}