[CodeForces-513E2]Subarray Cuts

题目大意:
  给你一个数列,从中选出k个互不重叠的非空子串,定义s[i]为第i个子串的和,求|s[1]-s[2]|+|s[2]-s[3]|+...+|s[k-1]-s[k]|的最大值。

思路:
  考虑将绝对值去掉,对于连续一段和单调的子串,结果只与其中峰值和谷值有关,中间的数会直接消掉。
  我们用f[i][j][0..3]表示在第i个数时总共选了j和子串时的状态。
  其中第三维为0时,表示当前子串为谷值。
  第三维为2时,表示当前子串为峰值。
  第三维为1时,表示当前子串在谷值到峰值之间。
  第三维为3时,表示当前子串在峰值到谷值之间。
  也不需要前缀和,因为你按每一个数DP,答案可以直接加上去。
  注意考虑边界情况,当j=1或k的时候只用加/减一次,当j!=1或k的时候,这个子串同时会时前、后两段的峰/谷值,要加/减两次。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<algorithm>
 4 inline int getint() {
 5     register char ch;
 6     register bool neg=false;
 7     while(!isdigit(ch=getchar())) if(ch=='-') neg=true;
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return neg?-x:x; 
11 }
12 const int _inf=-0x40000000;
13 const int N=30001,K=201;
14 int a[N];
15 int f[N][K][4];
16 int main() {
17     int n=getint(),k=getint();
18     for(register int i=1;i<=n;i++) {
19         a[i]=getint();
20     }
21     for(register int j=1;j<=k;j++) {
22         for(register int k=0;k<=3;k++) {
23             f[0][j][k]=_inf;
24         }
25     }
26     for(register int i=1;i<=n;i++) {
27         for(register int j=1;j<=k;j++) {
28             const int s=a[i]*((j!=1&&j!=k)?2:1);
29             f[i][j][0]=std::max(f[i-1][j][0],f[i-1][j-1][3])-s;//谷值 
30             f[i][j][1]=std::max(f[i-1][j][1],f[i][j][0]);//谷值-峰值 
31             f[i][j][2]=std::max(f[i-1][j][2],f[i-1][j-1][1])+s;//峰值 
32             f[i][j][3]=std::max(f[i-1][j][3],f[i][j][2]);//峰值-谷值 
33             if(j!=1&&j!=k) {//上一段也在峰值和谷值之间 
34                 f[i][j][1]=std::max(f[i][j][1],f[i-1][j-1][1]);
35                 f[i][j][3]=std::max(f[i][j][3],f[i-1][j-1][3]);
36             }
37         }
38     }
39     printf("%d
",std::max(f[n][k][1],f[n][k][3]));
40     return 0;
41 }
原文地址:https://www.cnblogs.com/skylee03/p/7649069.html