Codeforces Round 521 Div3 F1&F2

$n$ 个数的序列,要求每连续的 $k$ 个数至少选择一个数,一共选择 $x$ 个数的最大和。$(F1:n,k,x\le 200)$,$(F2:n,k,x\le 5000)$


题解

  • 定义dp[i][j]:前 $i$ 个数选择 $j$ 个数且第 $i$ 个数为第 $j$ 个选的数的最大值。
  • 显然对于 F1 直接 $n^3$ 转移即可。
  • 对于 F2,转移的时候有一个显然的单调栈优化。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5510;
const ll INF = 1e14+7;


int n, k, x;
ll a[maxn];
ll dp[maxn][maxn];
int que[maxn], head, tail;

int main()
{
scanf("%d%d%d", &n, &k, &x);

if((n/k) > x){
puts("-1");
return 0;
}

for(int i=1;i<=n;i++)
scanf("%lld", &a[i]);

ll maxx=0;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
dp[i][j]=-INF;
}
dp[0][0]=0;

for(int j=1;j<=x;j++)
{
head=tail=0;
que[tail++]=0;
for(int i=1;i<=n;i++)
{

while(tail-head && (i-que[head]>k)) head++;
if(tail != head)
dp[i][j]= a[i] + dp[que[head]][j-1];
else
dp[i][j]=0;
while( tail-head && (dp[que[tail-1]][j-1]<=dp[i][j-1])) tail--;
que[tail++]=i;




/*
for(int w=max(i-k, 0);w<i;w++)
{
if((w==0 && (j==1)) || dp[w][j-1]>0)
dp[i][j]=max(dp[w][j-1]+a[i], dp[i][j]);

}
*/
}
}

for(int i=max(n-k+1, 1);i<=n;i++)
{
maxx=max(dp[i][x], maxx);
}
printf("%lld\n", maxx);
}