Codeforces | CR #276 Div.1 – D.Kindergarten | 贪心
Description
给定一个数列,请你将其分为若干段。每一段的值定义为这一段数中最大值与最小值的差。请求出一种分法,使得这若干段的值的和最大。
Input Format
第一行一个正整数 $N$,代表数列长度。
第二行 $N$ 个整数,代表数列。
Output Format
一行一个正整数表示最大权值。
Input Sample
Sample 01
5
1 2 3 1 2
Sample 02
3
3 3 3
Output Sample
Sample 01
3
Sample 02
0
Data Range
$1 \le N \le 10^6$,$-10^9 \le a_i \le 10^9$。
Solution
因为求的是序列极差,若序列不是单调的,可以证明将其分为几个单调的子序列答案一定更优。故分成的子序列一定是一些单调的子序列。
考虑贪心决策将单调序列的分界点划分到前面一个序列还是后面。设 $F[i]$ 表示前 $i$ 个元素形成序列分成若干组的最大值。若 $a_{i-2} … a_i$ 是单调的,那么 $F[i] = F[i-1] + |a_i – a_{i-1}|$,否则 $F[i] = \max(F[i-1] , F[i-2] + |a_i – a_{i-1}|)$(左边是将 $F_{i-1}$ 划分到后面的序列,右边是将 $F_{i-1}$ 划分到后面的序列)。
Code
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define swap(x,y) {int t=x; x=y,y=t;}
#define wipe(x,y) memset(x,y,sizeof(x))
#define dbgIn(x) freopen(x".in","r+",stdin)
#define rep(x,y,z) for(int x=y,I=z;x<=I;++x)
#define dbgOut(x) freopen(x".out","w+",stdout)
#define file(x) freopen(x".in","r+",stdin); freopen(x".out","w+",stdout)
typedef long long ll;
inline void Read(int &x){
x=0; char ch=0; while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar(); return;
}
int N;
int src[1000005];
ll F[1000005];
bool Mono(int x){
if(src[x]>=src[x-1] && src[x-1]>=src[x-2])
return true;
if(src[x]<=src[x-1] && src[x-1]<=src[x-2])
return true;
return false;
}
int main(){
Read(N);
rep(i,1,N)
scanf("%d",&src[i]);
F[1]=0;
F[2]=abs(src[2]-src[1]);
rep(i,3,N){
if(Mono(i))
F[i]=F[i-1]+abs(src[i]-src[i-1]);
else
F[i]=max(F[i-1],F[i-2]+abs(src[i]-src[i-1]));
}
printf("%lld\n",F[N]);
return 0;
}