题目
非常废话
传送门
题目大意:
给你一个长度为n+1的格子,编号为[0,n],你在0,现在你要从0跳过n,每次你能跳到下一个格子的区间为[i+L,i+R],每个格子有一个价值A[i],求跳过n后使沿途格子价值和最大的最大值.
数据范围
对于60%的数据:
N
<
=
10
,
000
N <= 10,000
N<=10,000
对于100%的数据:
N
<
=
200
,
000
N <= 200,000
N<=200,000
对于所有数据
−
1
,
000
<
=
A
[
i
]
<
=
1
,
000
-1,000 <= A[i] <= 1,000
−1,000<=A[i]<=1,000且
1
<
=
L
<
=
R
<
=
N
1 <= L <= R <= N
1<=L<=R<=N
思路
首先,我们可以非常容易地写出:
f
[
i
]
=
m
a
x
{
f
[
j
]
}
+
a
[
i
]
(
m
a
x
(
i
−
R
,
0
)
<
=
j
<
=
i
−
L
)
f[i]=max\{f[j]\}+a[i](max(i-R,0)<=j<=i-L)
f[i]=max{f[j]}+a[i](max(i−R,0)<=j<=i−L)
但是这是一个
O
(
n
2
)
O(n^2)
O(n2)的Dp,对于这道题只能过部分分,于是我们就要往优化方面去想了。
我们发现,对于一个状态f[i]的f[j]所处的区间[i-R,i-L],是随我们i增大而不断向右一移动的,那我们就可以利用这一性质来进行滑窗,我们窗口的宽度是固定的,用优先队列存储状态时,只要顶部与i的距离大于(R-L+1)就说明无法转移,弹出就是了
这样之后复杂度就是
O
(
n
l
o
g
n
)
O(nlog_n)
O(nlogn)了
代码
#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
int f=1,x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
#define MAXN 200000
#define INF 0x3f3f3f3f
struct node{
int p,val;
friend bool operator < (node a,node b){return a.val<b.val;}
};
int a[MAXN+5],f[MAXN+5];
priority_queue<node> Q;
int main(){
int n=read(),L=read(),R=read();
for(int i=0;i<=n;i++)
a[i]=read();
for(int i=L;i<=n;i++){
Q.push((node){i-L,f[i-L]});
while(Q.top().p<i-R) Q.pop();
f[i]=Q.top().val+a[i];
}
int ans=-INF;
for(int i=n+1-R;i<=n;i++)
ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}