题意:求m个不相交子串,使得这些子串的和最大;
/*
1.基本思路:
首先,定义数组num[n],dp[m][n].
num[n]用来存储n个整数组成的序列.
dp[i][j]用来表示由前 j项得到的含i个字段的最大值,且最后一个字段以num[j]项结尾。仔细想想,我们可以知道:
dp[i][j]=max(dp[i][j-1]+num[j],dp(i-1,t)+num[j]) 其中i-1<=t<=j-1.
(因为必须是以 num[j] 结尾的,所以num[j]一定属于最后一个子段,即要么自己独立成一个子段,要么与前边以num[j-1]结尾的子段联合)
所求的最后结果为 max( dp[m][j] ) 其中1<=j<=n.
但是,我们会发现,当n非常大时,这个算法的时间复杂度和空间复杂度是非常高的,时间复杂度近似为O(m*n^2),
空间复杂度近似为O(m*n).因此,我们需要优化算法来降低时间复杂度和空间复杂度.
2.优化算法:
(1)节省时间
由基本思路,我们可以知道,dp[i][j]=max(dp[i][j-1]+num[j],dp(i-1,t)+num[j]),其中i-1<=t<=j-1.我们只要找到dp[i][j-1]
和dp[i-1][t]的最大值加上num[j]即为dp[i][j].所以,定义一个数组pre_max[n],用pre_max[j-1]来表示求解dp[i][j]时dp[i-1][t]
的最大值,则dp[i][j]=max(pre_max[j-1],dp[i][j-1])+num[j].
特别注意,pre_max[n]这个位置的存储空间是始终用不到的,因此可以用来存储其他数值,在接下来会用到。
在求解dp[i][j]的同时,我们可以计算出dp[i][t];i<=t<=j的最大值,这个最大值在计算dp[i+1][j+1]的时候需要作为pre_max[j]的
形式被使用,我们先把它存在pre_max[n]中。
你可能会问:为什么不把它直接放在pre_max[j]中呢?因为你接下来需要计算dp[i][j+1]的值,需要用到pre_max[j]中原来的值,
如果你把它存在这里,就会覆盖掉计算dp[i][j+1]所需要的那个值。所以,先把它放在pre_max[n]中。
当我们计算完dp[i][j+1]之后,就会发现pre_max[j]中的值已经没有用处了,我们可以把它更新为计算dp[i+1][j+1]所需要的那个值,
即之前放在pre_max[n]中的那个值,即执行pre_max[j]=pre_max[n].
这样我们就节省了计算最大值时付出的时间代价。
思路:设dp[i][j]表示前j个数(包括第j个数),分成i段的最大和,那么它的最优子结构可能来
自两部分,第一部分是当第j个数单独作为1段,第二部分是j和前面的连成一部分。那么
dp[i][j]=max(dp[i-1][j]+a[j],dp[i-1][k]+a[j])(k>=i-1&&k<=j-1)
再设g[i][j]表示前j个数,分成i段的最大和(此时第j个数可能包括也可能不包括)
g[i][j]=max(g[i][j-1],dp[i][j]);
通过g函数的定义,可以将dp的递推式优化为dp[i][j]=max(dp[i][j-1],g[i-1][j-1])+a[j];
g[i-1][j-1]表示的是,第j个单独成段,去掉第j个后,剩下的i-1段由剩下的j-1个数组成;
滚动数组:因为i从1到m,要计算dp[i][j]=max(dp[i][j-1],g[i-1][j-1])+a[j],我们只需要知道它
的前一轮数就行,而g[i-1]是前一轮推出来的,所以可以把i的数组直接滚动了。(能不能滚动,主
要是看计算当前状态需要的之前状态是什么,如果只需要前一轮的状态,那么只要保证在需要它之
前,它还没有被更新就行)
*/
//dp[i][n]=max(dp[i-1][n]+a[i],dp[i-1 ][n-1]+a[i])
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
using namespace std;
int a[1000005];
int dp[1000005];
int ma[1000005];
int main()
{
int m,n,ans;
while(scanf("%d%d",&m,&n)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
memset(dp,0,sizeof(dp));
memset(ma,0,sizeof(ma));
for(int j=0;j<m;j++)
{
ans=-inf;//确保比每个数都小
for(int i=j;i<n;i++)
{
dp[i]=max(dp[i-1]+a[i],ma[i-1]+a[i]);//ma为上一组dp的最优解,所以优化采用滚动数组
//表达dp[i][n]=max(dp[i-1][n]+a[i],dp[i-1][t]+a[i])
//dp[i-1]+a[i] dp[i-1][n]+a[i]
//ma[i-1]+a[i] dp[i-1][t]+a[i]
///printf("dp[%d]=%d dp[%d]=%d ma[%d]=%d\n",i,dp[i],i-1,dp[i-1],i-1,ma[i-1]);
ma[i-1]=ans;//滚动数组,ma为求下一组dp的最优解
///printf("ma[%d]=%d\n",i-1,ma[i-1]);
ans=max(ans,dp[i]);//判断最大值,解释为啥一开始ans为无穷小
//求出当前循环所能得到的最优解
///printf("ans=%d\n\n",ans);
}
}cout <<ans<< endl;
}
return 0;
}