1373D

题目链接: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;
}
原文地址:https://www.cnblogs.com/ZJNU-huyh/p/13502710.html