很经典的动态规划,Maxsum Plus
dp[i][j]表示将i个数字,分为j的段不相交子段的最大字段和,容易得到:
dp[i][j] = max(dp[i-1][j],dp[k][j-1])+a[i] k∈[j-1,i]
因为数据范围过大,对其方程式进行压缩:
dp[i-1][j]+a[i] 表示:将a[i]直接划入最后一个子段,字段数目不变
dp[k][j-1]+a[i]表示:将a[i]看做一个新增的子段,子段数目为原有子段数目+1
列出伪代码如下:
for(j=1;j<=m;j++)//枚举字段数
{
LL maxn = pre[j-1] //maxn存放 dp[k][j-1] (k为变量)的最大值
for(int i=j;i<=n-m+j;i++) //枚举能划分为j段的数字个数
{
dp[i] = max(dp[i-1],maxn)+a[i] //相当于从 (dp[i-1][j]+a[i],dp[k][j-1]+a[i])中选取最大值
if(maxn < pre[i]) //pre[i]用来存放,i个字符划分为不相交j-1段的最大字段和,故而应先更新maxn再更新pre
maxn = pre[i];
pre[i] = dp[j];//更新pre值
}
}
AC代码:
#include<stdio.h> #include<math.h> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #define MAXSIZE 1000005 #define INF 999999999 #define LL long long using namespace std; LL dp[MAXSIZE],pre[MAXSIZE],a[MAXSIZE]; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { for(int i=1;i<=n;i++) scanf("%lld",&a[i]); for(int i=1;i<=m;i++) { LL maxn = pre[i-1]; for(int j=i;j<=n-m+i;j++) { dp[j] = max(dp[j-1],maxn)+a[j]; if(maxn < pre[j]) maxn = pre[j]; pre[j] = dp[j]; } } LL ans = 0; for(int i=m;i<=n;i++) { if(ans < dp[i]) ans = dp[i]; } printf("%lld\n",ans); } return 0; }