用dp[i][j]代表前j个数中,以a[j]结尾,分成i组的最大和。
状态转移方程:dp[i][j]=max(max(dp[i-1][k])+a[j],dp[i][j-1]+a[j]) 其中(i-1<=k<=j-1)。
情况1:max(dp[i-1][k])+a[j]表示第j个数a[j]独自作为一组,前k个数中找出以a[k]为结尾,分成i-1组。这样正好有i组,同时,因为i-1组至少有i-1个数,至多有j-1个数,所以i-1≤k≤j-1。
情况2:dp[i][j-1]+a[j]表示第j个数加入以a[j-1]结尾的第i组。
但是,这样空间复杂度和时间复杂度都太大了。
通过观察,我们发现dp[i][j]仅仅使用到了dp[i-1]中的i-1到j-1的最大值,即 max(dp[i-1][k]),i-1≤k≤j-1。所以,我们可以用数组premax为记录dp[i][j]记录dp[i-1][k]中相应范围的最大值,这样就可以少一层循环,同时二维数组dp[i][j]变成了dp[j],dp[j]表示从以a[j]结尾的最大和。
所以,转换方程变成了dp[j]=max(premax[j-1],dp[j-1])+a[j]。premax[j-1]相当于情况1,max(dp[i-1][k]),i-1≤k≤j-1,a[j]独自一组,而dp[j-1]表示情况2,a[j]加入以a[j-1]为结尾的组。
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int M,N;
int a[2000000],dp[2000000],premax[200000];
int main()
{
while(scanf("%d%d",&M,&N)!=EOF)
{
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
}
memset(dp,0,sizeof(dp));
memset(premax,0,sizeof(premax));
int temp;
for(int i=1;i<=M;i++)
{
temp=-1e9;
for(int j=i;j<=N;j++)
{
// cout<<"d[j]=max(premax[j-1]("<<premax[j-1]<<"),dp[j-1]("<<dp[j-1]<<")])+a[j]=("<<a[j]<<")"<<'\n';
dp[j]=max(premax[j-1],dp[j-1])+a[j];
// cout<<"temp="<<temp<<'\n';
premax[j-1]=temp;//temp存放i~j-1中的最大值
// cout<<"temp=max(temp("<<temp<<"),dp[j]=("<<dp[j]<<")"<<'\n';
temp=max(temp,dp[j]);
}
}
printf("%d\n",temp);
}
}
动态规划感觉一直掌握的不好,自己写题目经常想不出状态的表示和状态转换。