思路:
首先我们可以将每相邻两项的差的绝对值算出来,然后按顺序形成一个新的数列:
如样例
1 4 2 3 1 --> 3 2 1 2
记新数组为 a
观察公式,选取某一起点元素u,终点元素v,则:
f(l,r) = a[u] - a[u+1] + a[u+2] - a[u+3] ... a[v]
即正负号依此出现,呈交错性。
因为起点u和终点v可以任意选,则原问题转化为:
给你两个数组,求任意连续区间和的最大值
对于样例,即为:
求 a1: 3 -2 1 -2 和 a2: -3 2 -1 2 中任意连续区间和的最大值。
然后就变成了一个经典的问题。
朴素的枚举起点终点 或者 枚举区间长度 都是O(n^2) 此题果断超时没商量。
此时有两种写法进行选择:
一 .DP: 对于某一个元素 i,其作为终点时连续区间和的最大值dp[i]满足 :
d
p
[
i
]
=
m
a
x
(
0
,
d
p
[
i
−
1
]
)
+
a
[
i
]
dp[i] = max(0,dp[i-1]) + a[i]
dp[i]=max(0,dp[i−1])+a[i]
二.处理前缀和 + 动态维护最小前缀和:
因为对于某一个元素 i,其作为终点时连续区间和的最大值dp[i]满足:
d
p
[
i
]
=
s
u
m
[
i
]
−
m
i
n
(
s
u
m
[
j
]
)
(
0
=
<
j
<
i
<
=
n
)
dp[i] = sum[i] - min(sum[j]) (0=< j<i<=n)
dp[i]=sum[i]−min(sum[j])(0=<j<i<=n)
其中sum为前缀和,sum[j]为 i 之前最小的前缀和。
for(int i=1 ;i<=n ;i++){ // sum[0] = 0
sum[i] = sum[i-1] + a[i];
}
for(int i=1 ;i<=n ;i++){
ans = max(ans,sum[i] - Min);
Min = min(Min,sum[i]);
}
然后本蒟蒻选的第一种DP写法:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int A = 1e5 + 100;
int a[A];
ll v_1[A],v_2[A];
ll dp_1[A],dp_2[A];
int main(){
int n;
scanf("%d",&n);
for(int i=1 ;i<=n ;i++){
scanf("%d",&a[i]);
}
int now = 1;
for(int i=1 ;i<n ;i++){
v_1[i] = now * abs(a[i+1] - a[i]);
v_2[i] = -now * abs(a[i+1] - a[i]);
now = -now;
}
ll ans = -1;
dp_1[0] = dp_2[0] = 0;
for(int i=1 ;i<n ;i++){
dp_1[i] = max((ll)0,dp_1[i-1]) + v_1[i];
dp_2[i] = max((ll)0,dp_2[i-1]) + v_2[i];
ans = max(ans,dp_1[i]);
ans = max(ans,dp_2[i]);
}
printf("%I64d\n",ans);
return 0;
}