题目描述
给定一个长 的数列 ,再给定一个 。你需要选中其中一些数,使得对于所有连续的被选中的长度至少为 的子串 满足:
求最多能选出多少个数。
思路
主要讲一下思考过程。首先肯定是将 全部减掉 。
接下来往下思考,所有的选中的长度大于等于 的子串,这个条件很棘手,考虑找一个等价的简化命题。不难发现,可以转化为所有长度等于 和 的子串和大于 ,显然地,所有的长度大于等于 的子串都可以分割为若干个长度为 或 的子串拼接起来。
然后我们发现,对于一个数字的决策,只跟前两个数字的决策有关,那么可以考虑动态规划。
设 分别表示 不取, 取但是 不取, 取 也取时的最大价值。
考虑转移。
对于 和 ,转移都是很显然的:
接下来看 的转移:
加入第 个是最后一段连续段的第 个或更多时,当 且 时才可以选,此时转移为:
加入第 个是最后一段连续段的第 个时,当 时才可以选,此时转移为:
然后这道题就做完了,时间复杂度为 。
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 10;
int T, A[MAXN], X, N;
int Dp[3][2];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> T;
while (T--) {
cin >> N;
A[0] = -1114514;
for (int i = 1; i <= N; i++) {
cin >> A[i];
}
cin >> X;
for (int i = 1; i <= N; i++) {
A[i] -= X;
}
memset(Dp, 0, sizeof Dp);
for (int i = 1; i <= N; i++) {
Dp[0][i & 1] = max({Dp[0][(i - 1) & 1], Dp[1][(i - 1) & 1], Dp[2][(i - 1) & 1]});
Dp[1][i & 1] = Dp[0][(i - 1) & 1] + 1;
if (A[i] + A[i - 1] >= 0) {
Dp[2][i & 1] = Dp[1][(i - 1) & 1] + 1;
if (i >= 2 && A[i - 2] + A[i - 1] + A[i] >= 0) {
Dp[2][i & 1] = max(Dp[2][i & 1], Dp[2][(i - 1) & 1] + 1);
}
}
}
cout << max({Dp[0][N & 1], Dp[1][N & 1], Dp[2][N & 1]}) << endl;
}
return 0;
}