题目:给n个数字,取m个不相交连续子段,和最大。n<1000000。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1024
s[i]:第i个数字(i=1->n)
状态:
dp[i][j]:前j个数字选取i个子段最大和,并且第i个子段包含s[j]。
状态转移方程:
dp[i][j]=max(dp[i][j-1]+s[j],dp[i-1][k]+s[i]) (i-1<=k<=j-1)
第j个元素要么和前一个组成一段,要么自成一段。
ans=max(dp[m][i]) 1<=i<=n
时间复杂度O(m*n*n)
空间复杂度O(m*n)
时间优化
pre_max[j-1] = max(dp[i-1][k]) (i-1<=k<=j-1)
pre_max[j-1]储存计算dp[i][j]时需要的max(dp[i-1][k]),降低时间复杂度
计算完dp[i][j]之后 更新pre_max[j]=max(pre_max[j],dp[i][j])
空间优化
dp[i][j]=max(dp[i][j-1]+s[j],dp[i-1][k]+s[i]) (i-1<=k<=j-1)
计算dp[i]时的dp[i-1]已不需要,dp数组可降至一维
AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1000000+5;
LL dp[maxn];
LL pre_max[maxn];
LL s[maxn];
int main()
{
//freopen("input.txt","r",stdin);
int m,n;
while(scanf("%d%d",&m,&n)!=EOF){
for(int i=1;i<=n;i++){
scanf("%I64d",&s[i]);
}
memset(pre_max,0,sizeof(pre_max));
for(int i=1;i<=m;i++){
dp[i] = dp[i-1]+s[i];
pre_max[n]=dp[i];
for(int j=i+1;j<=n;j++){
// dp[i][j] = dp[i][j-1]+s[j];
// for(int k=i-1;k<=j-1;k++){
// dp[i][j] = max(dp[i][j],dp[i-1][k]+s[j]);
// }
dp[j] = max(dp[j-1]+s[j],pre_max[j-1]+s[j]);
//一定注意是更新pre_max[j-1]
pre_max[j-1]=pre_max[n];
pre_max[n] = max(pre_max[n],dp[j]);
}
}
printf("%I64d\n",pre_max[n]);
}
return 0;
}