CF484D Kindergarten

CF484D Kindergarten

洛谷传送门

题意翻译

有一组数,你要把他分成若干连续段。每一段的值,定义为这一段 数中最大值与最小值的差。 求一种分法,使得这若干段的值的和最大。 N < 1e6, a[i] < 1e9。


题解:

正解:贪心+DP。

贪心策略:一定是单调连续串(递增递减都可以)使得最终加出来的和最优。否则都可以拆成单调串来处理。

然后第一感觉不需要DP,直接模拟然后累加就可以。但是发现不太对劲,因为对于序列的峰值和谷值来讲,我们不知道把它归属于前面最优还是后面更优。所以需要一个DP来保证每次决策的局部最优性,从而递推得出全局最优性。

显然,需要记录前方的峰值或谷值的位置,这里记为pos。

然后设(dp[i])表示前i个数所获得的最大价值。

那么转移方程就是:

[dp[i]=max(dp[pos]+|a[i]-a[pos+1]|,dp[pos-1]+|a[pos]-a[i]|) ]

也就是把pos值分别归属于前面的串和自己的贡献取较大。

然后再更新pos值即可。更新pos值的过程只需要用当前数前后各一个数来判就可以。

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e6+6;
ll n,a[maxn],dp[maxn],pos=1;
bool check(ll x)
{
	return (a[x]>=a[x-1]&&a[x]>=a[x+1])||(a[x]<=a[x-1]&&a[x]<=a[x+1]);
}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for(int i=2;i<=n;i++)
	{
		dp[i]=max(dp[pos]+abs(a[i]-a[pos+1]),dp[pos-1]+abs(a[pos]-a[i]));
		pos=check(i)?i:pos;
	}
	printf("%lld
",dp[n]);
	return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/14034374.html