题目描述
定义一个函数,函数的意义如下:
对于区间l,r,f(l,r)表示数列的第l项差分取正号,加上第l+1项差分取负号,加上l+2项差分取正号,以此类推累加到第r项差分。
1<=l,r<=n
|x|表示x的绝对值。
现在给你一个函数,请取恰当的l,r使f值最大,请输出最大的f值
输入格式
The first line contains single integer n ( 2<=n<=105 ) — the size of the array a .
The second line contains n integers a1,a2,...,an (- 10e9<=ai<=10e9 ) — the array elements.
输出格式
Print the only integer — the maximum value of f.
Sample Input
5
1 4 2 3 1
Sample Output
3
这道题明显采用动态规划的做法,求最大的f函数,容易想到最大区间和的做法,最大区间和中我们是令dp[i]表示以i结尾的最大区间和,那么递推过程里,要么选择以i-1结尾的最大区间和加上第i项,要么不要前面的和,单选第i项。
但是这道题里面,由于题目在区间和的过程中加入了正负号的转变,导致转移过程不能这么简单的二选一完事,还要考虑正负号,所以就有了下面的两种做法。
一,是采用二维dp,还是考虑以右端点最大的区间和,由于符号可能正也可能负,但是往左之后就是一正一负交替。所以用二维dp[i][j],表示第i位结尾,并且符号是j的最大区间和。转移过程就是:
dp[i][0]=max(dp[i-1][1]+del[i],del[i]),第i项如果符号位是0,i-1项结尾的符号是1,加上第i项的差分数组,与单独选第i项进行比较。同理,有dp[i][1] = max(0ll, dp[i - 1][0] - del[i]),而答案就是dp[n-1][0]和dp[n-1][1]中最大的那个,代码如下:
#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn = 2e5 + 10;
ll arr[maxn], del[maxn];
ll dp[maxn][2];//表示第i位结尾,且结尾的符号正负时的最大区间和
ll read() {
ll x = 0, f = 0, ch = getchar();
while (!isdigit(ch)) { if (ch == '-')f = 1;ch = getchar(); }
while (isdigit(ch)) { x = x * 10 + ch - '0';ch = getchar(); }
return f ? -x : x;
}
int main() {
int n = read();
for (int i = 1;i <= n;i++) {
arr[i] = read();
}
for (int i = 1;i <= n - 1;i++)
del[i] = abs(arr[i + 1] - arr[i]);
for (int i = 1;i <= n - 1;i++)
dp[i][0] = max(dp[i - 1][1] + del[i], del[i]),
dp[i][1] = max(0ll, dp[i - 1][0] - del[i]);
ll ans = 0;
for (int i = 1;i <= n;i++) {
ans = max(ans, dp[i][0]);
ans = max(ans, dp[i][1]);
}
cout << ans;
return 0;
}
第二种方法是由jzb同学提出来的,用两个dp的方式。顺着最大区间和的思路,还是用dp[i]表示以i结尾的最大条件区间和,那么能够确定的就是最后一项肯定是差分数组的最后一项,也就是dp[n-1]=del[n-1],往前递推时,在稿纸上会发现,前一项dp[n-2]写出来差分数组的正负号,刚好后面都是错开的,也就是dp[n-2]=del[n-2]-(n-1到结尾的区间和),那么我们想要让dp[n-2]最大,就需要让del[n-2]减去最小的后面区间和,所以就会想到再开一个dp数组,记录以i结尾的最小区间和,两个dp数组交替从后往前递推,而答案就是大的dp数组中的最大值。代码如下:
#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn = 2e5 + 10;
ll arr[maxn], del[maxn];
ll dp1[maxn];//表示第i位开始的最大区间和
ll dp2[maxn];//表示第i位开始的最小区间和
ll read() {
ll x = 0, f = 0, ch = getchar();
while (!isdigit(ch)) { if (ch == '-')f = 1;ch = getchar(); }
while (isdigit(ch)) { x = x * 10 + ch - '0';ch = getchar(); }
return f ? -x : x;
}
int main() {
int n = read();
for (int i = 1;i <= n;i++) {
arr[i] = read();
}
for (int i = 1;i <= n - 1;i++)
del[i] = abs(arr[i + 1] - arr[i]);
dp1[n - 1] = dp2[n - 1] = del[n - 1];//初始化
ll ans = dp1[n-1];
for (int i = n - 2;i >= 1;i--) {//从后向前递推
dp1[i] = max(del[i], del[i] - dp2[i + 1]);
ans = max(ans, dp1[i]);
dp2[i] = min(del[i], del[i] - dp1[i + 1]);
}
cout << ans << endl;
return 0;
}