51nod1052 最大M子段和

很经典的动态规划,Maxsum Plus

dp[i][j]表示将i个数字,分为j的段不相交子段的最大字段和,容易得到:

  dp[i][j] = max(dp[i-1][j],dp[k][j-1])+a[i] k∈[j-1,i]

因为数据范围过大,对其方程式进行压缩:

  dp[i-1][j]+a[i] 表示:将a[i]直接划入最后一个子段,字段数目不变

  dp[k][j-1]+a[i]表示:将a[i]看做一个新增的子段,子段数目为原有子段数目+1

  列出伪代码如下:

  for(j=1;j<=m;j++)//枚举字段数

  {

    LL maxn = pre[j-1] //maxn存放 dp[k][j-1] (k为变量)的最大值

    for(int i=j;i<=n-m+j;i++) //枚举能划分为j段的数字个数

    {

      dp[i] = max(dp[i-1],maxn)+a[i]  //相当于从 (dp[i-1][j]+a[i],dp[k][j-1]+a[i])中选取最大值

      if(maxn < pre[i]) //pre[i]用来存放,i个字符划分为不相交j-1段的最大字段和,故而应先更新maxn再更新pre

        maxn = pre[i];

      pre[i] = dp[j];//更新pre值

    }

  }

 

AC代码:

#include<stdio.h>
#include<math.h>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define MAXSIZE 1000005
#define INF 999999999
#define LL long long
using namespace std;

LL dp[MAXSIZE],pre[MAXSIZE],a[MAXSIZE];

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(int i=1;i<=n;i++)
            scanf("%lld",&a[i]);
        for(int i=1;i<=m;i++)
        {
            LL maxn = pre[i-1];
            for(int j=i;j<=n-m+i;j++)
            {
                dp[j] = max(dp[j-1],maxn)+a[j];
                if(maxn < pre[j])
                    maxn = pre[j];
                pre[j] = dp[j];
            }
        }
        LL ans = 0;
        for(int i=m;i<=n;i++)
        {
            if(ans < dp[i])
                ans = dp[i];
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2018-01-27 09:33  声声醉如兰  阅读(175)  评论(0编辑  收藏  举报