HDU 1024 Max Sum Plus Plus

本题的大致意思为给定一个数组,求其分成m个不相交子段和最大值的问题。

读完题就能看出要用DP,但是因为m的范围没有限定,= =加上自己太菜不知道怎么写, 总之先膜拜大佬http://www.cnblogs.com/kuangbin/archive/2011/08/04/2127085.html

读完大佬的代码还是有点懵,用自己容易理解的方式 稍微记录一下。

设dp[i][j]为,分为i段的时候,到第j个数字为止,且包含第j个数字的最大值。

进行状态转移的时候,当我们访问到第j个数字,我们需要判断它是独立成组,还是包含在已有的段内。

所以dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k])+a[j])  (i-1<=k<=j-1)

但是本题如果直接开双数组的话(n和m会达到的最大值太大),会编译失败。仔细观察的话,可以发现只与该段和前一段有关

所以我们每次只需要记录最近的两段即可,通过滚动数组进行优化。

本题用cin输入的话 耗时是比scanf多了近一倍    ,看了下别人的博客学到了一个好方法就是在该函数里面加std::ios::sync_with_stdio(false);  cin用时就会减少

另外,这个算法的复杂度是O(mn),题目中的m没有给出来,但测试数据中是一个较小的值(1000以下),所以能用这种方法AC。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <map>
#include <cstring>
using namespace std;
typedef long long LL;
const int MAXN=1e6+10;
const LL INF=1e18;
LL dp[MAXN];
LL a[MAXN];
LL maxx[MAXN];
int main()
{
    int m,n;
    LL tmp;
    while(scanf("%d%d",&m,&n)!=EOF)
   {
       if(m>100)while(1);
       memset(dp,0,sizeof(dp));
       memset(maxx,0,sizeof(maxx));
      for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);

      for(int i=1;i<=m;i++)
      {
          tmp=-INF;
          for(int j=i;j<=n;j++)
          {
            dp[j]=max(dp[j-1]+a[j],maxx[j-1]+a[j]);
            maxx[j-1]=tmp;//记录到j-1为止的最大值,留给下一段使用。
            tmp=max(dp[j],tmp);//记录该段目前为止的最大值,进行maxx数组的更新操作。
          }
      }
      printf("%lld
",tmp);
   }

    return 0;
}

---恢复内容结束---

原文地址:https://www.cnblogs.com/a249189046/p/6624928.html