传送门
把一个数组分成m个可不连续区间.求区间和最大值
-1 (4 ) -2 (3 -2 3) 分成2块 , max = 8;
思路:
状态:dp[i][j]表示把前j个数组分成i组的区间和。
转移方程:dp[i][j] = max(dp[i][j - 1], dp[i - 1][j - 1]) + a[j];
第一个状态表示前j - 1个数分成 i 组后将第 j 个数并入最后一个组,
第二个状态表示前j - 1个数分成 i - 1 组之后第 j 个数单独成一组。
因为二维dp会炸,需要优化:
可以发现dp[i - 1][j - 1]可以用一个pre数组记录, 则转移方程可以写成:
dp[i][j] = max(dp[i][j - 1], pre[j - 1]) + a[j];
此时又可以发现dp数组的第一维是对结果没有影响的,再改写转移方程为:
dp[j] = max(dp[[j - 1], pre[j - 1] + a[j];
而pre数组的更新则是:
pre[j - 1] = max(pre[j - 2], dp[j]).
const int MAXN = 1e6 + 10;
const int inf = 0x3f3f3f3f;
int m,n;
ll dp[MAXN],pre[MAXN],a[MAXN];
int main(){
//前 j 个数组分成 i 组 获得的最大值
//dp[i][j] = max(dp[i][j-1],dp[i-1][j-1]) + a[i];
while(~scanf("%d%d",&m,&n)){
memset(pre,0,sizeof(pre));
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
ll ans;
for(int i=1;i<=m;i++){
ans = -inf;
for(int j=i;j<=n;j++){
dp[j]=max(dp[j-1],pre[j-1]) + a[j];
pre[j-1] = ans;
ans = max(ans,dp[j]);
}
}
printf("%lld\n",ans);
}
return 0;
}