技巧1:f[i][j]
占用空间过大,可以考虑f[j][i]
能否优化掉一维
CF 1582 E. Pchelyonok and Segments
输入 t(t≤100) 表示 t 组数据。每组数据输入 n(1≤n≤1e5) 和长为 n 的整数数组 a (1≤a[i]≤1e9),所有数据的 n 之和不超过 1e5。从 a 中选尽可能多的互不相交的子数组,设有 k 个子数组,需满足:
1. 从左到右第一个子数组的长度恰好是 k,第二个的长度恰好是 k-1,……,最后一个的长度恰好是 1;
2. 从左到右第 i 个子数组的元素和严格小于第 i+1 个子数组的元素和。输出 k 的最大值。注:子数组是连续的。
输入 5 1 1 3 1 2 3 5 1 1 2 2 3 7 1 2 1 1 3 2 6 5 9 6 7 9 7 输出 1 1 2 3 1 |
---|
思路:f[i][j]
表示从i
开始的后缀,最大长度为j
的最小值。
然后会发现这种写法需要占用的空间太大,会MLE
考虑f[j][i]
然后优化掉j
这一维
static final int N = 100010;
static int[] a = new int[N];
static long[] s = new long[N];
static long[] f = new long[N], g = new long[N];
static int n;
static void solve() {
int T = ni();
while (T-- > 0) {
n = ni();
s[n + 1] = 0;
for (int i = 1; i <= n; i++)
a[i] = ni();
Arrays.fill(f, 0);
Arrays.fill(g, 0);
for (int i = n; i >= 1; i--) {
s[i] = s[i + 1] + a[i];
f[i] = Math.max(f[i + 1], a[i]);
}
int c = 1;
for (int k = 2; ; k++) {
g[n] = 0;
for (int i = n; i >= 1; i--) {
g[i] = g[i + 1];
if (i + k <= n && s[i] - s[i + k] < f[i + k])
g[i] = Math.max(g[i], s[i] - s[i + k]);
}
long[] t = g;
g = f;
f = t;
if (f[1] == 0) break;
c++;
}
out.println(c);
}
}