CodeForces 513E2 Subarray Cuts

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/81393482

题面

题意

给出一串数,在其中选出k个不重叠,不相交的子串,按顺序将子串的和标为s1,s2,s3…..sk,求|s1 - s2| + |s2 - s3| + … + |sk - 1 - sk|的最大值。

做法

首先贪心的想,我们肯定希望s中相邻的两个数,一个特别,另外一个特别小,我们将特别大的记为峰值,特别小的记为谷值,这样理想的s序列就是:……-峰-谷-峰-谷-……
那样将原式的绝对值去掉之后就是:±(s1-s2*2+s3*2-……..±sn)。
因此如果一个数是处于谷,则它对答案的贡献为-2*num(在头或尾上不用乘2)。
如果一个数是处于峰,则它对答案的贡献为+2*num(在头或尾上也同样不用乘2)。
那么我们就可以记dp[i][j][k]表示前i个数,分成了j段,此时状态为k,状态一共有四种:
1.此时取的是谷。
2.此时在谷和峰之间。
3.此时取的是峰。
4.此时在峰和谷之间。
状态转移为:

int tmp=1+(j!=1&&j!=m);
dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j-1][3])-tmp*num[i];
dp[i][j][1]=max(dp[i-1][j][1],dp[i][j][0]);
dp[i][j][2]=max(dp[i-1][j][2],dp[i-1][j-1][1])+tmp*num[i];
dp[i][j][3]=max(dp[i-1][j][3],dp[i][j][2]);

需要注意的是最终的答案不一定是……-峰-谷-峰-谷-……这样的理想序列,为了凑齐k个子串,可能在峰谷之间加入对答案毫无贡献的子串,所以在状态转移时要加上(注意这种转移不会出现在头尾):

int tmp=1+(j!=1&&j!=m);
if(tmp==2)
{
    dp[i][j][1]=max(dp[i][j][1],dp[i-1][j-1][1]);
    dp[i][j][3]=max(dp[i][j][3],dp[i-1][j-1][3]);
}

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 30010
#define M 210
using namespace std;

int n,m,dp[N][M][4],num[N];
//0 :gu
//1 :gu->feng
//2 :feng
//3 :feng->gu
int main()
{
    int i,j;
    cin>>n>>m;
    for(i=1;i<=n;i++) scanf("%d",&num[i]);
    memset(dp,-0x3f,sizeof(dp));
    for(j=0;j<=n;j++) for(i=0;i<4;i++) dp[j][0][i]=0;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=m;j++)
        {
            int tmp=1+(j!=1&&j!=m);
            dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j-1][3])-tmp*num[i];
            dp[i][j][1]=max(dp[i-1][j][1],dp[i][j][0]);
            dp[i][j][2]=max(dp[i-1][j][2],dp[i-1][j-1][1])+tmp*num[i];
            dp[i][j][3]=max(dp[i-1][j][3],dp[i][j][2]);
            if(tmp==2)
            {
                dp[i][j][1]=max(dp[i][j][1],dp[i-1][j-1][1]);
                dp[i][j][3]=max(dp[i][j][3],dp[i-1][j-1][3]);
            }
        }
    }
    cout<<max(dp[n][m][1],dp[n][m][3]);
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/81393482
今日推荐