http://acm.hdu.edu.cn/showproblem.php?pid=1024
这题可以这样来想:以dp[i][j]来表示前i个数字构成j段的最大和,则dp[i][j] = max(dp[i-1][j],max(dp[t][j-1])),其中0<t<i;即第i个数字和前面i-1个数字构成j段,第i个数字要么加入前一段,要么就单独构成一段.
但是这样写空间复杂度和时间复杂度都太高了。继续往下想:如果第i个数字加入前一段的话,就只与前一个数字的状态有关,则可以用一维的动态数组来保存。而
max(dp[t][j-1])可以用vis[j]来保存,每次dp[i][j]更新的时候就将vis[j]更新为最大值。
则状态转移方程变为:dp[j] = max(dp[j],vis[j-1])+num[i];
至此这题就可以解决了。
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define int64 __int64
#define ll long long
#define mod 1000000007
#define M 1000005
int m , n , num[M] , dp[M] , vis[M];
int Solve()
{
int i , j;
//初始化
fill(dp , dp+M , -(1<<30));
fill(vis , vis+M , -(1<<30));
vis[0] = dp[0] = 0;
for (i = 1 ; i <= n ; i++)
{
for (j = min(i,m) ; j >= 1 ; j--)//前i个数字最多组成min(i,m)个集合;
{
dp[j] = max(dp[j]+num[i],vis[j-1]+num[i]);
vis[j] = max(vis[j],dp[j]);
}
}
return vis[m];
}
int main()
{
int i;
while (~scanf("%d%d",&m,&n))
{
for (i = 1 ; i <= n ; i++)scanf("%d",num+i);
printf("%d\n",Solve());
}
return 0;
}