从CF1373D看最大子段和与奇偶段的分析

Problem - 1373D - Codeforces

先看出了一个很显然的东西,逆转的子序列的长度必须是偶数。

但之后就想错了,想到双指针和其他方法去求这个最大段。

但我粗暴的通过 ai+1ai 来贪心双指针明显是不对的。

1|0最大子段和

只要把 ai+1ai 转成一个数组 bi,发现问题就转化成了经典问题最大子段和

求一个序列连续段的最大和。

利用 dp 思想,设加上当前这个数之后,前缀和为 SUM

如果 SUM0,那么这个前缀仍然可能贡献最大答案,加上这个数没问题。

如果 SUM<0,那么这个前缀还不如不选得到的 0SUM0

然后每一次都更新答案。

for (auto& x : a) { sum += x; if (sum < 0) { sum = 0; } ans = std::max(ans, sum); }

2|0奇偶段问题

但我们发现如果只从 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

3|0代码实现

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'; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/18102425.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示