子串和再续(最大m段子串和)

描述
给你一个序列 S1, S2, S3, S4 ... Sx, ... Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). 我们定义
sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).现在给你一个 m(8>m>0&&m<n)你的任务是计算
sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) ;我们规定他是不相交的。
请输出m段最大和,比如:m = 2,n = 6 ,{-1 4 -2 3 -2 4} 它的结果是 9;
输入
输入 T,表示T组数据
第二行 分别是m,n;
输出
请输出m段最大和
样例输入
1
2 6
-1 4 -2 3 -2 4
样例输出
9


这道题求的就是最大m段的子串和。

计算b(i,j)=max{b(i,j-1)+a[j],max b(i-1,t)+a[j]  (i-1<=t<j) } ( 1 <= i <= m, i <= j <= n )

因为只需要用到数组b的第i-1行,所以可以用滚动数组,

max b(i-1, t )的值可以在计算i-1行时预先计算保存起来。


#include <stdio.h>
#include <string.h>
#define LL long long
#define INF 0x7fffffff
const int maxn = 1000005;
LL dp[maxn], past[maxn], a[maxn];   //数据范围
//1维保存的是分成i段1-j的的最优值
LL MaxSum ( int m, int n )
{
    if ( m < 1 || n < m )   //无法构成m个子串
        return 0;
    memset ( dp, 0, sizeof ( dp ) );
    memset ( past, 0, sizeof ( past ) );    //预处理 保存上一行前i个的最大值
    for ( int i = 1; i <= m; i ++ )
    {
        dp[i] = past[i-1]+a[i];
        LL max = dp[i];
        for ( int j = i+1; j <= n-m+i; j ++ )   //最少要留出m-i个数
        {
            dp[j] = ( dp[j-1] > past[j-1] ? dp[j-1] : past[j-1] )+a[j];
            //找上一行或当前行前一个的最优值
            past[j-1] = max;
            if ( max < dp[j] )
                max = dp[j];
        }
        past[n-m+i] = max;
    }
    LL ans = -INF;
    for ( int i = m; i <= n; i ++ )
        if ( dp[i] > ans )
            ans = dp[i];
    return ans;
}
int main ( )
{
    int n, m, T;
    scanf ( "%d", &T );
    while ( T -- )
    {
        scanf ( "%d%d", &m, &n );
        for ( int i = 1; i <= n; i ++ )
            scanf ( "%lld", &a[i] );
        printf ( "%lld\n", MaxSum ( m, n ) );
    }
    return 0;
}



你可能感兴趣的:(子串和再续(最大m段子串和))