【HDU 1024】Max Sum Plus Plus(DP+滚动数组优化+最大m段字段之和)

来源:互联网 发布:matlab数据分析案例 编辑:程序博客网 时间:2024/05/18 11:28

Description

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 S 1, S 2, S 3, S 4 … S x, … S n (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ S x ≤ 32767). We define a function sum(i, j) = S i + … + S j (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(i 1, j 1) + sum(i 2, j 2) + sum(i 3, j 3) + … + sum(i m, j m) maximal (i x ≤ i y ≤ j x or i x ≤ j y ≤ j x 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(i x, j x)(1 ≤ x ≤ m) instead. ^_^

Input

Each test case will begin with two integers m and n, followed by n integers S 1, S 2, S 3 … S n.
Process to the end of file.

Output
Output the maximal summation described above in one line.

Sample Input

1 3 1 2 3
2 6 -1 4 -2 3 -2 3

Sample Output

6
8

题目大意

本题的大致意思为给定一个数组,求其分成m个不相交子段和最大值的问题。

思路

首先把问题分解为子问题,对于某一个状态想要得到最优解,那么这个状态的前一个状态必定为最优解(如果问题可以分为一个个子问题),对于本题试想:如果当前状态为j个数字分成i段,那么要保证其最优,考虑如下两种情况:
①:前j-1个数字分成了i段,那么第j个数字应该拼接接在这i段之后,到达新的转态即j个数字分成i段。

(i,j-1)+NUMBER(j)-->(i,j)

②前j-1个数字分成了i-1段,那么第j个数字应该单独成段,才能到达新的状态即j个数字分成i段。

(i-1,j-1)&&NUMBER(j)-->(i,j)  (&&表示第j个数字单独成段)

要保证j个数字分成i段这个状态为最优,那么必须保证这个状态是由上述两种情况中i段之和最大的状态转换而来,即:

max((i,j-1)+NUMBER(j),(i-1,j-1)&&NUMBER(j))-->(i,j)

结合DP的思想可以定义dp数组dp[i][j]表示当前状态为j个数字分成i段,num[i]表示数列中第i个数,那么结合上述的结果可以得到状态转移方程:

dp[i][j]=max(max(dp[i-1][t])+num[j],dp[i][j-1]+num[j]) /*其中t的取值范围是:0<t<j;这边有必要解释一下max(dp[i-1][t])+num[j]和dp[i][j-1]+num[j],后者很容易理解就是将num[j]拼接在这i段之中。对于前者,因为要保证状态最优,所以必须先找到dp[i-1][t](i-1<t<j)中最大的一个,注意dp[i-1][j-1]肯定也是由他的前一个最优状态得来的,但是这个最优状态不一定代表着i-1段中有j-1个数字,只是说这j-1个数字分成了i-1段,因为要最优所以必定要舍弃一些数字保证子段和最大,也就是说dp[i-1][j-1]此时不一定是最大的(但是是这个状态最优的)。所以当我们将第i个数字单独成段时,必须找到dp[i-1][?]中最大的一个才能保证最优。*/

理解了这些再来考虑优化问题:

①:为什么要优化呢?我们来看数据范围:0<m<=n<1000000对于上面的做法,首先需要跑i:1->m表示分成1~m这些段,接着跑j:0->n表示j个数字分成i段,再接着跑t:i-1->n寻找max(dp[i-1][t]),可以近似的认为时间复杂度为:O(n^3),肯定超时。空间复杂度此处未知。②怎么优化呢?时间上:我们发现对于max(dp[i-1][t]),我们其实只要在前面的过程中把最大的记录在一个数组pre[t]中,那么就可以不用跑一遍循环而可以直接使用。空间上:因为最终的转态都是由一个个小状态逐步转换而来的,而转态是由最小的开始的,所以对于dp[i][j]我们可以优化为一维数组dp[j],因为我们在跑for(i:1->m)时是从1开始跑的,当跑到2时dp[j]中的数据就是dp[1][j]中的数据,我们可以直接使用。详细实现可以参见代码!

代码

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>using namespace std;const int maxn=1000000+5;const int INF=0x3fffffff;int dp[maxn],num[maxn],n,m,pre[maxn];int main(){    while(~scanf("%d %d",&m,&n))    {        memset(pre,0,sizeof(pre));        int maxx;        for(int i=1;i<=n;i++)        {            scanf("%d",&num[i]);        }        dp[0]=0;        for(int i=1;i<=m;i++)        {            maxx=-INF;            for(int j=i;j<=n;j++)            {                dp[j]=max(dp[j-1],pre[j-1])+num[j];                pre[j-1]=maxx;                if(dp[j]>maxx) maxx=dp[j];            }        }        printf("%d\n",maxx);    }    return 0;}
0 0
原创粉丝点击