题意:
将含有n(10^6)个元素的序列a划分成几段 每段为连续的一些元素 每段的价值为段中最大值减去最小值 总价值为所有段的价值和 求 最大的总价值
思路:
不难想到一个dp的转移方程 dp[i] = max( dp[j] + max(a[j+1]...a[i]) - min(a[j+1]...a[i]) ) 但是dp是n^2的会TLE
注意观察转移方程 其中的max-min的部分很有特点 因为只关心这段区间的max和min 所以其他值都是没必要的 那么没有必要的值如果放在区间的端点 则将它划分到区间外一定会得到一个不更坏的答案 因此可以想到 max和min一定是区间的端点 那么这个区间是单调的 这个结论也很容易想出了
很明显 这样单调的区间应该划分在一起 可以画数轴看一下 不划分在一起就会少覆盖
至此已经得出本题的策略 单调的区间分一组
考虑细节 端点!! 样例 3 5 2 告诉我们端点应该特别考虑 因此我们可以在端点处特判它是放在左边区间更优还是右边区间更优 然后就可以O(n)的dp了 每次转移只与单调区间端点有关
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 1000010
int a[N];
int n;
LL dp[N], ans;
int cmp(int x, int y) {
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int l = 1; l < n;) {
int r = l + 1;
int sign = cmp(a[l], a[r]);
dp[r] = max(dp[r], dp[l]);
r++;
while (r <= n) {
if (sign != cmp(a[r - 1], a[r]))
break;
dp[r] = max(dp[r], dp[r - 1]);
r++;
}
r--;
for (int i = max(1, l); i <= l + 1 && i < r; i++) {
for (int j = max(r - 1, i + 1); j <= r + 1 && j <= n; j++) {
dp[j] = max(dp[j], dp[i - 1] + abs(a[j] - a[i]));
}
}
l = r;
}
cout << dp[n] << endl;
return 0;
}