51 nod 1052 最大M子段和(DP)

来源:互联网 发布:微信开发开源框架 php 编辑:程序博客网 时间:2024/06/06 05:30

1052 最大M子段和
基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
 收藏
 关注
N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个子段,并且这M个子段的和是最大的。如果M >= N个数中正数的个数,那么输出所有正数的和。
例如:-2 11 -4 13 -5 -2,分为2段,11 -4 13一段,6一段,和为26。
Input
第1行:2个数N和M,中间用空格分隔。N为整数的个数,M为划分为多少段。(2 <= N , M <= 5000)第2 - N+1行:N个整数 (-10^9 <= a[i] <= 10^9)
Output
输出这个最大和
Input示例
7 2-211-413-56-2
Output示例
26

这题难就难在怎么构造DP方程,一开始用dp[i][j] i 表示个数 j 表示段数没转移过来

后来用 i 表示段数,j 表示个数能成功转移

dp[i][j] 只存在两种转移方式 一种是dp[i-1][k] +a[j] 一种是dp[i][k]+sum[k+1][j]

#include <cstdio>#include <cstring>#include <algorithm>#include <bits/stdc++.h>using namespace std;const int N = 5005;typedef long long LL;const LL mod = 1e9+7;const LL inf = 0x3f3f3f3f;LL sum[N], a[N];LL dp[2][N], pre[2][N];int main(){    int n, m;    scanf("%d %d", &n, &m);    if(m>n)m=n;    sum[0]=0;    for(int i=1; i<=n; i++)    {        LL x;        scanf("%lld", &x);        a[i]=x;        sum[i]=sum[i-1]+x;    }    memset(dp,-0x3f,sizeof(dp));    pre[0][0]=-inf;    for(int i=1;i<=n;i++)    {        dp[1][i]=max(a[i],dp[1][i-1]+a[i]);        pre[0][i]=max(pre[0][i-1],dp[1][i]);    }    int o=0;    for(int i=2;i<=m;i++)    {        int flag=0,h=0;        LL maxt;        o^=1;        memset(pre[o],-0x3f,sizeof(pre[o]));        for(int j=i;j<=n;j++)        {            dp[i&1][j]=pre[o^1][j-1]+a[j];            if(flag)dp[i&1][j]=max(dp[i&1][j],sum[j]+maxt);            if(flag)            {                if(pre[o][j-1]<dp[i&1][j])pre[o][j]=dp[i&1][j];                else pre[o][j]=pre[o][j-1];                maxt=max(maxt,dp[i&1][j]-sum[j]);            }            else pre[o][j]=dp[i&1][j],maxt=dp[i&1][j]-sum[j];            flag=1;        }    }    LL ans=-inf;    for(int i=m;i<=n;i++) ans=max(ans,dp[m&1][i]);    cout<<ans<<endl;    return 0;}

原创粉丝点击