题目

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

题解

由于每个状态有两个数要考虑,因此考虑使用二元的动态规划
dp[i][j] 表示到以第 i 个数字结尾,分成 j 组的最大和

当循环到数字 i 时,有以下几种情况:

  • 将数字加入到已选取的组中
    dp[i][j] = dp[i - 1][j] + a[i]
  • 将数字放到新的一组(上一组可能在之前的任意位置)
    dp[i][j] = max{dp[k][j - 1] + a[i]}

数据最大可能需要开 1000000 的数组,二维的 dp 开出来妥妥爆炸
(按照题目,最坏情况下甚至需要 long long )

先压缩内存
首先可以看出来记录组数的这一维其实只用到了当前操作的和上一次操作的数据
因此只需要 2*1000000 就足够存储数据了

然后是时间优化
直接模拟上面的代码,显然时间复杂度是 O(n<sup>3</sup>)
可以看出我们需要的其实是上一次的数据到 i 的最大值(不包括 dp[i] )
因此,在处理 dp[i] 时顺便维护最大值存储到数组里即可

具体操作如下( int 足以应付数据)

LL Max;
for(int j = 1;j <= m;j++) {
    Max = -INF;
    for(int i = j;i <= n;i++) {
        dp[i] = max(dp[i - 1] + a[i],dp1[i - 1] + a[i]);
        dp1[i - 1] = Max;
        Max = max(dp[i],Max);
    }
}

代码

/*
By:OhYee
Github:OhYee
HomePage:http://www.oyohyee.com
Email:oyohyee@oyohyee.com

かしこいかわいい?
エリーチカ!
要写出来Хорошо的代码哦~
*/

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <vector>
#include <list>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;

typedef int LL;

const LL INF = 0x7FFFFFFF / 2;

const int maxn = 1000005;
const int maxm = 1000005;

LL dp[maxm],dp1[maxm];
int a[maxn];
int v;

bool Do() {
    int n,m;
    if(scanf("%d%d",&m,&n) == EOF)
        return false;

    dp[0] = dp1[0] = 0;
    for(int i = 1;i <= n;i++) {
        scanf("%d",&a[i]);
        dp[i] = 0;
        dp1[i] = 0;
    }

    LL Max;

    for(int j = 1;j <= m;j++) {
        Max = -INF;
        for(int i = j;i <= n;i++) {
            dp[i] = max(dp[i - 1] + a[i],dp1[i - 1] + a[i]);
            dp1[i - 1] = Max;
            Max = max(dp[i],Max);
        }
    }

    printf("%d\n",Max);
    return true;
}

int main() {
    while(Do());
    return 0;
}