题目大意:
给你一个数列,从中选出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 }