先看出了一个很显然的东西,逆转的子序列的长度必须是偶数。
但之后就想错了,想到双指针和其他方法去求这个最大段。
但我粗暴的通过 \(a_{i + 1} - a_i\) 来贪心双指针明显是不对的。
最大子段和
只要把 \(a_{i + 1} - a_{i}\) 转成一个数组 \(b_i\),发现问题就转化成了经典问题最大子段和
求一个序列连续段的最大和。
利用 dp 思想,设加上当前这个数之后,前缀和为 \(SUM\)
如果 \(SUM \ge 0\),那么这个前缀仍然可能贡献最大答案,加上这个数没问题。
如果 \(SUM < 0\),那么这个前缀还不如不选得到的 \(0\),\(SUM \gets 0\)
然后每一次都更新答案。
for (auto& x : a) {
sum += x;
if (sum < 0) {
sum = 0;
}
ans = std::max(ans, sum);
}
奇偶段问题
但我们发现如果只从 \(i = 0\) 开始枚举段样例都过不了。
这是因为这类枚举奇\偶段问题要分两次枚举才能枚举完所有奇偶段
- 从 \(0\) 开始枚举完
- 从 \(1\) 开始枚举完
这是很显然的,比如:
1 3 4 5 6 7
显然我们要包括所有偶数段,如果只从 0 开始
1 3, 1 3 4 5, 1 3 4 5 6 7
少了一些段,所以还要再从 \(1\) 开始
3 4, 3 4 5 6
代码实现
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (auto& x : a) std::cin >> x;
i64 ans{};
for (int i = 0; i < n; i++) {//先把原来偶数位上都加起来
if (i % 2 == 0) {
ans += a[i];
}
}
i64 mxPre{};
auto getMxPre = [&](auto& a) -> void {//最大子段和问题
i64 pre{};
i64 _mxPre{};
for (auto& x : a) {
pre += x;
if (pre < 0) {
pre = 0;
}
_mxPre = std::max(_mxPre, pre);
}
mxPre = std::max(mxPre, _mxPre);
};
std::vector<int> deal0, deal1;
for (int i = 0; i + 1 < n; i += 2) {
deal0.push_back(a[i + 1] - a[i]);
}
for (int i = 1; i + 1 < n; i += 2) {
deal1.push_back(a[i] - a[i + 1]);//这里是因为从1开始枚举每次开头的都是奇数
}
getMxPre(deal0); getMxPre(deal1);
std::cout << ans + mxPre << '\n';
}
标签:std,奇偶,pre,子段,auto,枚举,CF1373D,mxPre
From: https://www.cnblogs.com/kdlyh/p/18102425