题意
给定一个算式,这个算式有\(n\)个正整数,并有\(n-1\)个运算符符将其连接,且只有可能是\(+\)或\(-\)
请给这个算式添加若干个合法的(这里的合法指的是数学中的合法)括号,使得这个算式的运算结果最大
解法
很有意思的一道题
然而我并没有想出来
首先把连续的用\(+\)号连接的正数合到一起,可以很方便的证明这样做对答案没有影响
处理后的数组有如下的性质
- 每个\(+\)号前后一定是\(-\)号
- 若有两个连续的\(-\)号,如果在第一个\(-\)号后添上括号,之后的所有数字都可以取到正值(具体来说,首先我们会损失第一个数字,但我们可以用括号将所有\(+\)号连接的两个数括起来,这样第一个数字后的所有数字都可以为正)
以上运用了一点贪心的思想
接下来我们从后往前进行\(DP\)
\(a[i]>0\)
加不加括号都没关系
\(f[i]=a[i]+f[i+1]\)
\(a[i]<0\)
不加括号
\(f[i]=a[i]+f[i+1]\)
加一个括号
\(a[i+1]<0\)
根据之前提到的性质
\(f[i]=max(f[i], a[i]+sum[i+1])\)
\(a[i+1]>0\)
这样我们不得不损失前两个数,但是这个正数后一定是一个负数(之前的性质)
\(f[i]=max(f[i], a[i]-a[i+1]+sum[i+2])\)
这里的\(sum[i]\)指的是后缀绝对值和
这样\(DP\)后输出\(f[1]\)即可
代码
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int n, cnt, T;
long long a[N], b[N], f[N], sum[N];
long long abS(long long x) {
return x < 0 ? -x : x;
}
int main() {
// freopen("jerry.in", "r", stdin);
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int ch = getchar(), f = 1;
a[i] = 0;
while (!isdigit(ch))
ch == '-' ? f = -1, ch = getchar() : ch = getchar();
while (isdigit(ch))
a[i] = a[i] * 10 + ch - 48, ch = getchar();
a[i] *= f;
}
cnt = 0;
for (int i = 1; i <= n; ++i) {
b[++cnt] = a[i];
while (b[cnt] > 0 && a[i + 1] > 0 && i < n) b[cnt] += a[++i];
}
n = cnt;
sum[n + 1] = 0;
for (int i = n; i >= 1; --i)
sum[i] = sum[i + 1] + abS(b[i]);
f[n] = b[n];
for (int i = n - 1; i >= 1; --i) {
if (b[i] >= 0)
f[i] = b[i] + f[i + 1];
else {
if (b[i + 1] < 0)
f[i] = b[i] + sum[i + 1];
else
f[i] = b[i] - b[i + 1] + sum[i + 2];
f[i] = max(f[i], b[i] + f[i + 1]);
}
}
printf("%lld\n", f[1]);
}
return 0;
}
/*
1
3
5 - 1 - 3
*/