题目链接:https://codeforces.com/problemset/problem/1373/D
题面:
题意:给你一列数,第一个数字认为是0为偶数位,你有一次倒置一个区间字符串的机会,希望选一个区间倒置后让你统计所有偶数位的数字和最大。
分析:不得不说cf上一些题目的质量真的高,尤其是教育场,下次一定咕咕咕
题解:①首先你需要知道,不论起点是偶数位还是奇数位,只要倒置一段长度为奇数的区间,显然不会影响偶数位之和,所以一定是倒置一个偶数长度的区间。
②如果倒置一个长度为2n的区间,你可以把它转化成不重叠且连续的n个长度为2的区间。
③显然,如果只考虑区间长度为2,那显然这段区间之前的值,不会影响答案(dp的后效性),就可以用dp或者类dp的贪心。
④我们就记录初始数列已有的偶数位之和为sum,倒置区间后比原来多的值记录在dp数组中。(dp细节看代码)
⑤我们发现例如数列{a0,a1,a2}例如:1 100 2,倒置区间[0,1]的答案与倒置区间[1,2]的答案显然不同,所以左换和右换两者都要考虑,取一个max即可。
⑥dp是在原基础上的增加值,所以仅在偶数位操作。不妨设dp_l:dp_l[i]记录的是前i位中所有偶数位进行左换操作后,使答案增加的最优情况,dp_r同理。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; inline ll read() { int x = 0, f = 1; char c = getchar(); while (c<'0' || c>'9') { if (c == '-')f = -f; c = getchar(); } while (c >= '0' && c <= '9')x = x * 10 + c - '0', c = getchar(); return x * f; } ll num[200005]; ll dp_l[200005];//dp_l[i]记录的是前i位中所有偶数位进行左换操作后,使答案增加的最优情况 ll dp_r[200005];//右换,前i位使答案增加的最优情况 ll sum;//基准值 int main(void) { ll t = read(); while (t--) { sum = 0; ll max_ans = 0; ll n = read(); for (ll i = 0; i < n; i++) { dp_l[i] = 0; dp_r[i] = 0; } for (ll i = 0; i < n; i++) { num[i] = read(); if (i % 2 == 0)sum += num[i]; } for (ll i = 0; i < n; i++)//左换 { if (i == 0)dp_l[0] = 0; else if (i % 2 == 0)dp_l[i] = max(0ll, dp_l[i - 2] + num[i - 1] - num[i]); max_ans = max(max_ans, dp_l[i]); } for (ll i = 0; i < n; i++)//右换 { if (i == 0)dp_r[1] = max(0ll, num[1] - num[0]); else if (i % 2 == 0)dp_r[i + 1] = max(0ll, dp_r[i - 1] + num[i + 1] - num[i]); max_ans = max(max_ans, dp_r[i]); } cout << max_ans + sum << endl; } return 0; }