Pokémon Army (easyversion) -每天一把CF - 20201007

    科技2025-01-17  5

    每天一把CF : 2020-10-07

    文章目录

    题目思路代码实现

    题目

    原题链接:https://codeforc.es/problemset/problem/1420/C1

    (今天CF官网抽风,一直在维护,下面的题目描述我是找的别人的博客里的,是hard版本的,但是也基本知道easy版本是什么样子了) 题目已补

    思路

    题目大意:给你n个数,可以从中选任意个数(至少要选一个)组成一个新的数组,这个新的数组的价值计算方式为所有奇数索引项之和减去所有偶数索引项之和,即a1-a2+a3-a4+…问能得到的字数列最大价值是多少。

    我们将给定的n个数化成如下的折线图:

    我们首先给出结论->我们要找的那些点就是图中的峰点和谷点。->证明放在思路的最后

    再考虑如何求出这个新的数组的值:

    我们先只考虑如下的abcd四项,其值明显为a-b+c-d=(a-b)+(c-d)

    而a-b的值又等于a到b中每一个小段的长度之和,而每一个小段的长度又等于a[i]-a[i+1]

    再注意我们取的a-b和c-d都是下降趋势的线段,所以只有当a[i]-a[i+1]>0时我们才计算进价值中。

    所以总的价值计算方式为:

    for (int i = 2; i <= n; i++) ans += max(0, a[i] - a[i + 1]);//a是全局变量,a[n+1]==0

    ->注意最后一个点的处理方式

    再说一句,我们取的点的个数一定是奇数的,因为偶数的话最后一个点是减,反而将价值变小了,不如不取。

    若最后一个点是偶数点,则我们不取,将其值加回-- c - d + d

    若是奇数点,我们需要加上其值

    接下来对我们的结论进行证明:

    若在我们选定的点中任意两个点之间再选取其余点,则其最终加到价值上的小段数回减少,所以我们原来的取法是最优的。

    代码实现

    #include <iostream> #include <algorithm> using namespace std; #define ll long long const int MAX = 3E5 + 5; int t, n; int a[MAX]; int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> t; while (t--) { memset(a, 0, sizeof(a)); cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; int ans = 0; for (int i = 2; i <= n; i++) { ans += max(0, a[i] - a[i + 1]); } cout << ans << endl; } return 0; }
    Processed: 0.010, SQL: 8