题意
有 个数,每个数取值范围为
。现在要在两两数之间插入
和
两种运算符,使得运算结果最大化,求最大化的结果。
其中,。
分析
这里提供一种非主流做法。
最终的形式肯定是一块块乘积相加的形式,于是我们考虑 。
令 表示来到
时的最大值。
那么 。其中,
指的是
这段区间每个数的乘积。
由于 的取值只有
三种情况,我们分情况考虑:
- 乘积为
,也就是这段区间中包含了至少
个
,那这段区间一定包含了离
最近的
,不妨记这个位置为
,那么
- 乘积不为
,那么决策点一定是在
中进行选择。
如果,那么最优的方式肯定是从
转移过来,即
。
如果,那么可以有两种转移方式:
第一种是。也就是直接从
转移过来。
第二种是找到上一个的位置
,那么
这段区间的乘积为
,
。
可以发现上面的转移是可以保证答案最优的。
由于 和
是递增的,复杂度可以做到
。(当然用线段树也可以)
代码如下
#include <bits/stdc++.h>
using namespace std;
int read(){
int x, f = 1;
char ch;
while(ch = getchar(), ch < '0' || ch > '9') if(ch == '-') f = -1;
x = ch - '0';
while(ch = getchar(), ch >= '0' && ch <= '9') x = x * 10 + ch - 48;
return x * f;
}
const int N = 100005;
int a[N], f[N], val;
int main(){
int i, j, n, m, T, p, las;
T = read();
while(T--){
n = read();
for(i = 1; i <= n; i++) f[i] = 0, a[i] = read();
p = las = val = 0;
for(i = 1; i <= n; i++){
if(a[i] == 0){
while(p < i) val = max(val, f[++p]);
}
f[i] = f[i - 1] + a[i];
if(p > 0) f[i] = max(f[i], val);
if(a[i] == -1){
if(las > p) f[i] = max(f[i], f[las - 1] + 1);
las = i;
}
}
printf("%d\n", f[n]);
}
return 0;
}
京公网安备 11010502036488号