题意
有 个数,每个数取值范围为
。现在要在两两数之间插入
和
两种运算符,使得运算结果最大化,求最大化的结果。
其中,。
分析
这里提供一种非主流做法。
最终的形式肯定是一块块乘积相加的形式,于是我们考虑 。
令 表示来到
时的最大值。
那么 。其中,
指的是
这段区间每个数的乘积。
由于 的取值只有
三种情况,我们分情况考虑:
- 乘积为
,也就是这段区间中包含了至少
个
,那这段区间一定包含了离
最近的
,不妨记这个位置为
,那么
- 乘积不为
,那么决策点一定是在
中进行选择。
如果,那么最优的方式肯定是从
转移过来,即
。
如果,那么可以有两种转移方式:
第一种是。也就是直接从
转移过来。
第二种是找到上一个的位置
,那么
这段区间的乘积为
,
。
可以发现上面的转移是可以保证答案最优的。
由于 和
是递增的,复杂度可以做到
。(当然用线段树也可以)
代码如下
#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; }