题目传送门:选择数字
解题方向
DP+单调队列.
具体思路
首先我们对每一个数分析,易得它们只存在两种状态:选和不选.
∴很容易得到状态转移方程:
s
u
m
[
i
]
{sum[i]}
sum[i]表前缀和,i−k+1<=j<i;
(
f
[
i
]
[
0
]
/
f
[
i
]
[
1
]
{f[i][0]/f[i][1]}
f[i][0]/f[i][1]表(不)选第i个数时时最大值)
f
[
i
]
[
0
]
=
m
a
x
(
f
[
i
−
1
]
[
0
]
,
f
[
i
−
1
]
[
1
]
)
{f[i][0]=max(f[i-1][0],f[i-1][1])}
f[i][0]=max(f[i−1][0],f[i−1][1]);
f
[
i
]
[
1
]
=
m
a
x
(
f
[
i
]
[
1
]
,
f
[
j
]
[
0
]
+
s
u
m
[
i
]
−
s
u
m
[
j
]
)
{f[i][1]=max(f[i][1],f[j][0]+sum[i]-sum[j])}
f[i][1]=max(f[i][1],f[j][0]+sum[i]−sum[j]).
通过仔细观察可以发现:
f
[
i
]
[
0
]
{f[i][0]}
f[i][0]和
f
[
i
]
[
1
]
{f[i][1]}
f[i][1]的值只与
s
u
m
[
i
]
−
s
u
m
[
j
]
{sum[i]-sum[j]}
sum[i]−sum[j]相关;
∴DP转移方程又可简化为:
f
[
i
]
=
m
a
x
(
f
[
j
]
+
s
u
m
[
i
]
−
s
u
m
[
j
]
)
{f[i]=max(f[j]+sum[i]-sum[j])}
f[i]=max(f[j]+sum[i]−sum[j]);
又∵
s
u
m
[
i
]
{sum[i]}
sum[i]确定,由此可得:
f
[
i
]
=
m
a
x
(
f
[
j
]
−
s
u
m
[
j
]
)
+
s
u
m
[
i
]
{f[i]=max(f[j]-sum[j])+sum[i]}
f[i]=max(f[j]−sum[j])+sum[i];
用单调队列维护
m
a
x
(
f
[
j
]
−
s
u
m
[
j
]
)
{max(f[j]-sum[j])}
max(f[j]−sum[j])即可.
*注:此题可以帮助萌新们很好地理解单调队列的用法,建议反复品味.
代码
(简洁而迅速)
#include<bits/stdc++.h>
#define in read()
#define re register int
#define int long long
using namespace std;
int n,k,ans=-1;
int Dp[150005];
int a[100005],sum[100005];
int d[100005],q[100005];
int head=0,tail=1;
inline int que(int i)
{
d[i]=Dp[i-1]-sum[i];//记录要进单调队列的部分
while(head<=tail&&d[q[tail]]<d[i])tail--;//在新值之前又比它小的只肯定不是可行最大值
q[++tail]=i;//加入新值
while(head<=tail&&q[head]<i-k)head++;//超过可取范围则舍掉
return d[q[head]];//第一个必为合法最大值
}//单调队列优化
inline int in{
int i=0;char ch;
while(!isdigit(ch)){ch=getchar();}
while(isdigit(ch)){i=(i<<3)+(i<<1)+(ch-'0');ch=getchar();}
return i;
}//快读优化
signed main()
{
n=in,k=in;
for(re i=1;i<=n;i++)
{
a[i]=in;
sum[i]=sum[i-1]+a[i];
}
for(re i=1;i<=n;i++)Dp[i]+=que(i)+sum[i];
printf("%lld\n",Dp[n]);
return 0;
}
qwq.