题目链接:https://vjudge.net/contest/347398#problem/E
翻译:
输入两个数m和n,接下来有n个数。
sum(i,j)=a[i]+a[i+1]+…+a[j]的和。
求找出m个(i,j)使得 sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im, jm) 最大。
这m个区间不能相交
对于2 6 -1 4 -2 3 -2 3这组数据,输出8,选择的区间为4 -2 3和3这两个区间。
分析:
w[i][j]: 前 j 个数分为 i 段,第 j 个数必须选可以分成的情况:
1 第 j 个数单独为1段
2. 第 j 个数与前面的数连一块。
w[i][j] = max(b[i-1][j-1], w[i][j-1]) + a[j]
b[i][j]:前 j 个数分为 i 段,第 j 个数可选可不选可以分成的情况:
1.选第 j 个数;
2.不选第 j 个数。
b[i][j] = max(b[i][j-1], w[i][j])
为什么可以利用滚动数组优化?
仔细观察递归式
w[i][j] = max(b[i-1][j-1], w[i][j-1]) + a[j]
b[i][j] = max(b[i][j-1], w[i][j])
发现,对于取 i 段,w[i][j] 只与 b[i-1][j-1] 和 w[i][j-1]有关, 与之前的那一些项没有关系
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000010;
typedef long long LL;
int n,m;
LL sum[N],dp[2][N],w[N];
int main()
{
while(~scanf("%d%d",&m,&n))
{
sum[0]=0;
for(int i=1; i<=n; i++)
{
LL x;
scanf("%lld",&x);
sum[i]=sum[i-1]+x;
dp[0][i]=0;/*从前i个元素中取0段,最大值为0*/
}
for(int i=1; i<=m; i++)
{
for(int j=i; j<=n; j++)/*分成i组,最少需要i个数*/
{
if(i==j)
dp[i&1][j]=w[j]=sum[j];
else
{
w[j]=max(dp[(i-1)&1][j-1],w[j-1])+sum[j]-sum[j-1];
dp[i&1][j]=max(dp[i&1][j-1],w[j]);
}
}
}
printf("%lld\n",dp[m&1][n]);
}
return 0;
}