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;
}