最大M子段和

描述

给定一个整数序列S1 ,S2 ,·,Sn (1 ≤ n ≤ 1,000,000,−32768 ≤ Si ≤32768),定义函数

sum(i,j) = Si + ...+ Sj (1 ≤ i ≤ j ≤ n)。

现给定一个正整数 m,找出 m 对 i 和j,使得 sum(i1 ,j1 ) + sum(i2 ,j2 ) + ... + sum(im ,jm ) 最大。这就是最大 M 子段和(maximum m segments sum)。

输入每个测试用例由两个正整数 m 和 n开头,接着是 n 个整数。

输出

每行输出一个最大和。

样例输入

1 3 1 2 3

2 6 -1 4 -2 3 -2 3

样例输出

6

8



分析

设状态为 d[i,j],表示前 j 项分为i 段的最大和,且第 i 段必须包含 S[j],则状态转移方程如下:

d[i,j] = max{d[i,j −1] + S[j],max{d[i − 1,t] + S[j]}}, 其中i ≤ j ≤ n,i − 1 ≤ t < j

target = max{d[m,j]},其中m ≤ j ≤ n

分为两种情况:

• 情况一,S[j] 包含在第 i段之中,d[i,j − 1] + S[j]。

• 情况二,S[j]独立划分成为一段,max{d[i − 1,t] + S[j]}。

观察上述两种情况可知 d[i,j]的值只和 d[i,j-1] 和 d[i-1,t] 这两个值相关,因此不需要二维数组,

可以用滚动数组,只需要两个一维数组,用d[j] 表示现阶段的最大值,即 d[i,j − 1] + S[j],用prev[j] 表示上一阶段的最大值,即max{d[i − 1,t] + S[j]}。



代码

mmss.c

#include<stdio.h>

#include<stdlib.h>

#include<limits.h>

/**

* @brief 最大 m 段子序列和

* @param[in] S 数组

* @param[in] n 数组长度

* @param[in] m m 段

* @return 最大 m 段子序列和

*/

int mmss(int S[], intn, int m) {

int max_sum, i, j;

/* d[i]表示现阶段最大值,prev[i] 表示上阶段最大值 */

/* d[0], prev[0] 未使用*/

int *d = (int*)calloc(n + 1, sizeof(int));

int *prev = (int*)calloc(n + 1, sizeof(int));

S--; // 因为 j 是从 1开始,而 S 从 0 开始,这里要减一

for (i = 1; i <=m; ++i) {

   max_sum = INT_MIN;

   for (j = i; j <=n; ++j) {

      // 状态转移方程

      if (d[j - 1] <prev[j - 1])

        d[j] = prev[j - 1] +S[j];

      else

        d[j] = d[j - 1] +S[j];

      prev[j - 1] =max_sum; // 存放上阶段最大值

      if (max_sum <d[j])

        max_sum = d[j]; // 更新max_sum

   }

   prev[j - 1] =max_sum;

}

free(d);

free(prev);

return max_sum;

}


int main() {

 

 int n, m, i, *S;

 while(scanf("%d%d", &m, &n) == 2) {

   S = (int*)malloc(sizeof(int) * n);

   for (i = 0; i < n;++i)

     scanf("%d",&S[i]);

   printf("%d\n",mmss(S, n, m));

   free(S);

 }

 return 0;

}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值