题意:给定一个数组,求其分成m个不相交子段和最大值的问题。
思路:设Num为给定数组,n为数组中的元素总数,Status[i][j]表示前i个数在选取第i个数的前提下分成j段的最大值(在下面程序中用mmax[]数组表示),其中1<=j<=i<=n && j<=m,状态转移方程为:
Status[i][j]=Max(Status[i-1][j]+Num[i],Max(Status[0][j-1]~Status[i-1][j-1])+Num[i])
乍看一下这个方程挺吓人的,因为题中n的限定范围为1~1,000,000而m得限定范围没有给出,m只要稍微大一点就会爆内存。但仔细分析后就会发现Status[i][j]的求解只和Status[*][j]与Status[*][j-1]有关所以本题只需要两个一维数组即可搞定状态转移。
在进行更进一步的分析还会发现其实Max(Status[0][j-1]~Status[i-1][j-1])根本不需要单独求取。在求取now_Status(保存本次状态的数组)的过程中即可对pre_Status(保存前一次状态的数组)进行同步更新。
#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define MAXN 1000000
#define INF 0x7fffffff
int dp[MAXN+10];
int mmax[MAXN+10];
int a[MAXN+10];
int main()
{
int n,m;
int i,j,mmmax;
while(scanf("%d%d",&m,&n)!=EOF)
{
for(i=1; i<=n; i++)
{
scanf("%d",&a[i]);
mmax[i]=0;
dp[i]=0;
}
dp[0]=0;
mmax[0]=0;
for(i=1; i<=m; i++)
{
mmmax=-INF;
for(j=i; j<=n; j++)
{
dp[j]=max(dp[j-1]+a[j],mmax[j-1]+a[j]);
mmax[j-1]=mmmax;
mmmax=max(mmmax,dp[j]);
}
}
printf("%d\n",mmmax);
}
return 0;
}