Description
给定一个数组,求其分成m个不相交子段和最大值
Input
多组输入,每组用例占一行,首先输入两个整数m和n表示子段数和序列长度,之后n个整数表示该序列,以文件尾结束输入
Output
对于每组用例,输出序列最大m子段和
Sample Input
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
Sample Output
6
8
Solution
最大m子段和问题,经典dp
now[j]表示以第j个元素为结尾的i个子段的最大和,必须包含a[j]
pre[j]表示前j个元素i个子段的最大和,不一定包含a[j]。
dp[i][j]表示前j个元素i个子段的最大和,包含a[j]
原始状态转移方程:
dp[i][j]=max(dp[i][j-1],dp[i-1][k])+a[j] (i-1<=k<=j-1)
第1种情况是直接将第j个元素加在第i个子段之后,第2种情况是将第j个元素单独作为一个子段,那么前面必须是i-1个子段
这个题目由于n比较大,所以开不出dp数组,只有用滚动数组实现上述状态转移方程,用ans代替dp[i][j],则有下列状态转移方程
now[j]=max(now[j-1],pre[j-1])+a[j]
pre[j-1]=ans
ans=max(ans,now[j])
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define maxn 1000005
#define INF 1<<29
using namespace std;
int a[maxn],now[maxn],pre[maxn],ans;
int get_maxm(int n,int m)
{
//初始化
memset(now,0,sizeof(now));
memset(pre,0,sizeof(pre));
for(int i=1;i<=m;i++)//枚举段数
{
ans=-INF;//初始化
for(int j=i;j<=n;j++)
{
now[j]=max(now[j-1],pre[j-1])+a[j];
pre[j-1]=ans;
if(ans<now[j])
ans=now[j];
}
}
return ans;
}
int main()
{
int n,m;
while(~scanf("%d%d",&m,&n))
{
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
printf("%d\n",get_maxm(n,m));
}
return 0;
}