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\n",tmp); } return 0; }
---恢复内容结束---