Now I think you have got an AC in Ignatius.L’s “Max Sum” problem. To be a brave ACMer, we always challenge ourselves to more difficult problems. Now you are faced with a more difficult problem.
Given a consecutive number sequence S1, S2, S3, S4 … Sx, … Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). We define a function sum(i, j) = Si + … + Sj (1 ≤ i ≤ j ≤ n).
Now given an integer m (m > 0), your task is to find m pairs of i and j which make sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + … + sum(im, jm) maximal (ix ≤ iy ≤ jx or ix ≤ jy ≤ jx is not allowed).
But I`m lazy, I don’t want to write a special-judge module, so you don’t have to output m pairs of i and j, just output the maximal summation of sum(ix, jx)(1 ≤ x ≤ m) instead.
Each test case will begin with two integers m and n, followed by n integers S1, S2, S3 … Sn.
Process to the end of file.
Output the maximal summation described above in one line.
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
6
8
求长度为n,节数为m的最大字段和。(大家可以先看下这篇滚动数组的文章)
通常动态规划,我们都是先考虑最后一个状态结果,看看能不能继续递推状态。
所以我们先根据题目,先推出最容易想到的一个状态。
我们考虑到j位置:
1、它要么属于当前循环组数中的第j个位置
2、要么就是新的一组中的头一个数字,但是前面的组中,我们需要考虑有多少个数字,i-1组的话,最少也要i-1个数字,最多可能有j-1个数字
所以我们可以将最大字段和表示为dp[i][j],i代表第i组,j代表第j个位置
for(int i=1;i<=m;i++) //最原始的dp
{
for(int j=1;j<=n;j++)
{
mx=-1;
for(int k=i-1;k<=j-1;k++)
mx=max(mx,dp[i-1][k]+a[j]); //找出i-1到j-1位置中最大的那个
dp[i][j]=max(dp[i][j-1]+a[j],mx); //再与dp[i-1][j-1]+a[j]做比较
}
}
因为题目范围是1e6,二维数组,加三重循环,很明显会MLE
不过最起码的思路是有了,代码也十分的简洁,我们可以想方法来具体优化一下空间、时间复杂度。
首先我们由式子dp[i][j]=max(dp[i][j-1]+a[j],max(dp[i-1][k]+a[j])),k属于[i-1,j-1],
不管是前者还是后者,都是根据到j-1位置的最大字段和来计算的,
我们完全可以用一个一维dp来存贮到j位置的最大字段和即可,即表示为dp[j]的形式。
我们又会发现,我们这里其实需要的只是上一行的数据,也就是dp[i-1][j-1]最大字段和的数据,
我们完全可以在开一个pre数组来不断记录上一行的数据,再用mx记录下到j位置的最大字段和,
一旦使用完pre[j-1]中的数据,我们就用mx数据将其更新,达到滚动数组的效果
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
const ll inf = 0x3f3f3f3f;
ll a[maxn],dp[maxn],pre[maxn],mx;
int main()
{
ll n,m;
while(scanf("%lld %lld",&m,&n)!=EOF)
{
for(int i=1;i<=n;i++) //init
{
scanf("%lld",&a[i]);
dp[i]=0;
pre[i]=0;
}
for(int i=1;i<=m;i++) //优化后的dp
{
mx=-inf;
for(int j=i;j<=n;j++) //i组的话,至少需要i个数字
{
dp[j]=max(dp[j-1]+a[j],pre[j-1]+a[j]); //调用pre,此时的pre[j-1]存贮的数据是max(dp[i-1][x],x属于[i-1,j-1]),
pre[j-1]=mx; //我们再将pre[j-1]更新为mx中存储的到j-1位置的最大字段和
mx=max(mx,dp[j]); //再将mx更新到j位置时最大的字段和
}
}
printf("%lld\n",mx); //因为dp[j]表示加上j位置时得到的最大字段和,而题意时是求m个字段的最大字段和
//所以我们要判断的其实是max(dp[i][k]),k属于[i,j],故输出的是mx而不是dp[j]
}
return 0;
}