-
描述
-
给你一个序列 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
-
输入 T,表示T组数据
这道题求的就是最大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;
}