[CF788A] Functions Again(动态规划)

题目描述

定义一个函数,函数的意义如下:

对于区间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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值