由于 $a_i \in [-30,30]$,所以我们可以考虑枚举区间出现的最大值。由于可以只选一张牌,所以最大得分的最小值为 $0$。

那么我们首先枚举 $i \in [1,30]$,表示该段区间的最大值为 $i$,然后内层循环求和,即 sum += a[j]。由于最小值为 $0$,所以 sum = max (sum,0) 可以排除掉区间和为负数的情况。又因为第一重循环中已经限制了最大值,所以当出现 a[j] > i 的情况时,也要将 sum 更新为 $0$。答案的更新显然有 ans = max (ans,sum - i),该语句成立的充分条件是序列中出现了 $i$。只需要再用一个数组记录出现数字的情况即可,注意负数的情况(我就是因为这个错误 $\texttt{RE}$ 了一次 qwq),同时还需要注意该数组在 sum 更新时也要同时更新。

最后来简单看一下代码:

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
#include <iostream>
#include <cstdio>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int MAX = 1e5 + 5;
const int MOD = 1e7 + 9;
inline int read ();
int a[MAX],sum,n,ans;
bool f[50];
int main ()
{
n = read ();
for (int i = 1;i <= n;++i) a[i] = read ();
for (int i = 1;i <= 30;++i)
{
sum = 0;
for (int j = 1;j <= 30;++j) f[j] = 0;
for (int j = 1;j <= n;++j)
{
sum += a[j];
if (sum < 0 || a[j] > i) sum = 0,f[i] = 0;//不符合条件就将 sum 置为 0
else if (a[j] > 0) f[a[j]] = 1;//标记数字,注意筛去非正数
sum = max (sum,0);
if (f[i]) ans = max (ans,sum - i);//i 出现过
}
}
printf ("%d\n",ans);
return 0;
}
inline int read ()
{
int s = 0;int f = 1;
char ch = getchar ();
while ((ch < '0' || ch > '9') && ch != EOF)
{
if (ch == '-') f = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * f;
}

后记。看了别的 $\texttt{dalao}$ 的题解,发现 f[i] 的设置有些多余,因为即使最大值 $i$ 没有出现,那么相当于被多减,答案一定不会最优。