POJ2479 最大子段和

来源:互联网 发布:图片剪切软件下载 编辑:程序博客网 时间:2024/05/16 23:48

实例三、最大子段和 

问题表述 

n个数(可能是负数)组成的序列a1,a2,…an.求该序列

例如:  序列(-2,11,-4,13,-5,-2) ,最大子段和:

       11 - 4 + 13=20。

(1)穷举算法: O(n3), O(n2)

(2)分治法:

将序列a[1:n]从n/2处截成两段:a[1:n/2], a[n/2+1:n]

实例三、最大子段和

问题表述

n个数(可能是负数)组成的序列a1,a2,…an.求该序列 子序列的最大值。

也就是

 

例如:  序列(-2,11,-4,13,-5,-2) ,最大子段和:

     11 - 4 + 13=20。

(1)穷举算法: O(n3), O(n2)

(2)分治法:

将序列a[1:n]从n/2处截成两段:a[1:n/2], a[n/2+1:n]

一共存在三种情况:

a.最大子段和出现在左边一段

b.最大子段和出现在右边一段

c.最大子段和跨越中间的断点

对于前两种情况,只需继续递归调用,而对于第三种情况:

那么S1+S2是第三种情况的最优值。

(3)动态规划法:

定义b[j]:

含义:从元素i开始,到元素j为止的所有的元素构成的子段有多个,这些子段中的子段和最大的那个。

那么:

如果:b[j-1] > 0, 那么b[j] = b[j-1] + a[j]

如果:b[j-1] <= 0,那么b[j] = a[j]

这样,显然,我们要求的最大子段和,是b[j]数组中最大的那个元素。

实现:



这个题目我们需要分别求从0-i项的最大连续子序列和,从i-最后一项的最大连续子序列和,然后枚举一遍找出和最大的即可,总是简单的BUG调好久。


#include <iostream>#include <stdio.h>using namespace std;#define N 50007#include <cstring>#include <cmath>#define INF 0x3f3f3f3fint cases;int n;int a[N];int lt[N];int rt[N];int lres[N];int rres[N];int solve() {    lt[0] = a[0];    for (int i=1; i<n; i++) {        if (lt[i-1] <= 0) {            lt[i] = a[i];        } else {            lt[i] = lt[i-1] + a[i];        }    }    lres[0] = lt[0];    for (int i=1; i<n; i++) {        lres[i] = max(lres[i-1], lt[i]);    }    rt[n-1] = a[n-1];    for (int i=n-2; i>=0; i--) {        if (rt[i+1] <= 0) {            rt[i] = a[i];        } else {            rt[i] = rt[i+1] + a[i];        }    }    rres[n-1] = rt[n-1];    for (int i=n-2; i>=0; i--) {        rres[i] = max(rres[i+1], rt[i]);    }    int maxer = -INF;    //从这两个中间选出那个最大的    for (int i=0; i<n-1; i++) {        maxer = max(maxer, lres[i] + rres[i+1]);    }    //cout << lres[6] << " " << rres[7] <<endl;    return maxer;}int main() {    //freopen("in.txt","r",stdin);    scanf("%d", &cases);    while (cases--) {        scanf("%d", &n);        for (int i=0; i<n; i++) { scanf("%d", &a[i]); }        cout << solve() << endl;    }}


0 0
原创粉丝点击