思路:
考虑二维的定义f[i][j]表示前i位,分成j组的最大区间和那么容易写出转移方程
1.和前一位分在同一组 f[i][j]=f[i-1][ j ]+a[ j ];
2.和前一位分在不同组 f[i][j]= f[ k ][ j-1 ] + a[ j ] 0< k <i
n=1000000挂(吐槽,为嘛不给出m范围,知道我想On的转移想得多辛苦吗?)
然后可以发现其实每一位的转移只和 j 和 j-1有关把数组滚动起来优化 一维,接下来是对于k的优化
一开始的思想比较稚嫩,可以跳过此段:
既然k是从1到i,但是我们在转移的时候也会从1枚举到i所以没必要每次都向前找,记录一个前面的f[k][j-1]的最大值用Max记录下来就好了,但是,考虑一个问题,每一次转移以后,数组滚动以后有相当一部分的值是没有变动的,一个不注意 越界就很难处理,虽然最后还是调出来了,加了一个 前缀和:
#include<cstdio>
#include<cstring>
#include<iostream>
#define cmax(a,b) (a=max((a),(b)))
#define maxn 1000020
using namespace std;
int n,m,a[maxn],f[2][maxn],sum[maxn];
int main(){
while(scanf("%d%d",&m,&n)!=EOF){
memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)scanf("%d",a+i),sum[i]=sum[i-1]+a[i];
int pos=0;
int Max=-1e9,ans=-1e9;
for(int j=1;j<=m;j++){
pos^=1;
Max=f[pos^1][j-1],f[pos][j-1]=sum[j-1];
for(int i=j;i<=n;i++){
f[pos][i]=max(Max+a[i],f[pos][i-1]+a[i]);
cmax(Max,f[pos^1][i]);
if(j==m&&i>=m)cmax(ans,f[pos][i]);
}
}
printf("%d\n",ans);
}
return 0;
}
既然开始的容易出错,我考虑重新定义状态,定义f[i][j]表示前i个数 (不一定以i结尾,只要是在i之前结尾就好了)分成 j 组的最大和,第二维的优化一样,同样是维护一个Max不过Max是指分成当前的 j 组之前的最大和,然后和前一位比较得出最大值,然后更新当前数组和之前的,感觉其实代码没什么区别 ,重要的是dp的一种抽象的思想(可以细细体会前后两种不同,还是很有意思的)
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 1000020
#define cmax(a,b) (a=max((a),(b)))
using namespace std;
int n,a[maxn],f[maxn],dp[maxn],m;
int main(){
while(scanf("%d%d",&m,&n)!=EOF){
memset(f,0,sizeof(f)),memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)scanf("%d",a+i);
int Max=-1e9;
for(int i=1;i<=m;i++){
Max=-1e9;
for(int j=i;j<=n;j++){
f[j]=max(f[j-1]+a[j],dp[j-1]+a[j]);
dp[j-1]=Max;
cmax(Max,f[j]);
}
}
printf("%d\n",Max);
}
return 0;
}