E. Pchelyonok and Segments
题意
给定长度为的数组,要求将数组分成段,每段连续,并且对于段和,段的长度要比段长, 并且段的段内元素和要比段的小.求最大的 首先将数组反向,问题就变成了求长度递增的段,区间和递减。
得到状态表示为前i个元素中,最后一段长度为的最大末尾段和。
有状态转移方程:
若且,有
这里需要十分注意边界条件
代表空集里面最后一段长度是的方案中结尾段的最大值,显然不存在,不可以转移到任何状态,设为
代表前个里面选0个,任意选的状态都可以从其转移过来,应该设为可以转移的边界,故设为
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5 + 5, M = 450;
#define inf 0x3f3f3f3f3f3f3f3f
ll f[N][M], a[N];
void solve() {
int n; cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
reverse(a + 1, a + 1 + n);
for(int i = 1; i <= n; i ++) a[i] += a[i - 1];
for(int i = 1; i < M; i ++) f[0][i] = -inf;
for(int i = 0; i <= n; i ++) f[i][0] = inf;
for(int k = 1; k < M; k ++) {
for(int i = 1; i <= n; i ++) {
f[i][k] = f[i - 1][k];
if(i - k >= 0) {
if(a[i] - a[i - k] < f[i - k][k - 1])
f[i][k] = max(f[i][k], a[i] - a[i - k]);
}
}
}
int ans = 0;
for(int i = 1; i < M; i ++) if(f[n][i] > 0) ans = i;
cout << ans << endl;
}
int main() {
int t; cin >> t;
while(t --) {
solve();
}
}