51nod 1050 循环数组最大子段和(最大连续子段问题)

来源:互联网 发布:程序员进阶之路 编辑:程序博客网 时间:2024/06/05 18:24
N个整数组成的循环序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续的子段和的最大值(循环序列是指n个数围成一个圈,因此需要考虑a[n-1],a[n],a[1],a[2]这样的序列)。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000)第2 - N+1行:N个整数 (-10^9 <= S[i] <= 10^9)
Output
输出循环数组的最大子段和。
Input示例
6-211-413-5-2
Output示例
20


最大连续子段和问题:

给定一个n个数的序列,有正有负,求连续子序列和的最大值。一般的想法是枚举所有的区间进行更新一个最大值,O(n^2)的复杂度。但是事实上有一种更高效的算法可以再O(n)的时间内得到答案。算法如下:

用一个变量sun保存序列的实时序列和,当sum变成负数之后,就舍弃前一段序列,重新开始累积和,然后中途用ans更新最大值,一遍扫完就可以得到答案。

可以自己举一个有疑问的例子试试就明白算法是正确的,因为算法保证将任意一个贡献为正的序列永远记录在内,而对于贡献为负的序列,第一时间舍弃,保证不会影响后面的求和。

题解:这道题就是连续子段和的一个变形,如果序列不能循环那么就是最大子段和问题,但是可以循环,也就是解有可能存在a[n-1], a[n], a[0]...这样的序列中,但是这样的序列出现,就说明中间序列存在一个和为负的序列。如果将数字都取相反数,再跑一遍就可以得出中间部分负序列的和,不过是正的了。这样最后的解就变为 ans = max(ans1, ans2+sum)。详见代码。


#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<cstring>
#include<cstdio>
#include<utility>
#include<algorithm>
#include<map>
#include<stack>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
const int Hash = 10000;
const int INF = 1<<30;
const ll llINF = 1e18;


int n;
int in[maxn];
//连续序列的最大和
ll max_seq(int arr[])
{
    ll ans = 0, sum = 0;
    for(int i=0; i<n; i++)
    {
        sum += arr[i];
        if(sum < 0)
            sum = 0;
        else
            ans = max(sum, ans);
    }
    return ans;
}
int main( )
{
    //freopen("input.txt", "r", stdin);
    while(~scanf("%d", &n))
    {
        ll sum = 0;//存整个序列的和
        for(int i=0; i<n; i++)
        {
            scanf("%d", in+i);
            sum += in[i];
        }
        ll ans1 = max_seq(in);
        for(int i=0; i<n; i++)
            in[i] = -in[i];
        ll ans2 = max_seq(in);
        cout<<max(ans1, sum+ans2)<<endl;
    }
    return 0;
}

原创粉丝点击