最大子段和-分治&&动态规划

简介:        N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。 例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。 Input 第1行:整数序列的长度N(2 <= N <= 50000)第2 -
 
 
   N个整数组成的序列a[1],a[2],a[3],…,a[n], 求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
Output
输出最大子段和。
Input 示例
6
-2
11
-4
13
-5
-2
Output 示例
20
// 1.穷举50000显然超时,时间复杂度为O(n^2).
#include<iostream>
#include<cstdio>
using namespace std;
int a[50005];
int main()
{
   int i,j,n;
   while(~scanf("%d",&n))
   {
        for(i=0;i<n;i++)
            scanf("%d",a+i);
            __int64 max=0;
            for(i=0;i<n;i++)
            {
                __int64 sum=0;
                for(j=i;j<n;j++)
                {
                    sum+=a[j];
                    if(sum>max)
                        max=sum;
                }
            }
        printf("%I64d\n",max);
   }
    return 0;
}

/* 2.分治法  时间复杂度为O(nlogn).

求子区间及最大和,从结构上是非常适合分治法的,因为所有子区间[start, end]只可能有以下三种可能性:

在[1, n/2]这个区域内

在[n/2+1, n]这个区域内

起点位于[1,n/2],终点位于[n/2+1,n]内

以上三种情形的最大者,即为所求. 前两种情形符合子问题递归特性,所以递归可以求出. 对于第三种情形,则需要单独处理.

第三种情形必然包括了n/2和n/2+1两个位置,这样就可以利用第二种穷举的思路求出:

以n/2为终点,往左移动扩张,求出和最大的一个left_max

以n/2+1为起点,往右移动扩张,求出和最大的一个right_max

left_max+right_max是第三种情况可能的最大值

*/

#include<iostream>
#include<cstdio>
using namespace std;
int a[50005];
__int64 maxInterval(int *a,int l,int r)
{
    if(l==r)
        return a[l]>0?a[l]:0;
    int mid=(l+r)/2;
    __int64 leftmaxInterval=maxInterval(a,l,mid);
    __int64 rightmaxInterval=maxInterval(a,mid+1,r);
    int i;
    __int64 sum=0;
    __int64 left_max=0;
    for(i=mid;i>=l;i--)
    {
        sum+=a[i];
        if(sum>left_max)
            left_max=sum;
    }
    sum=0;
    __int64 right_max=0;
    for(i=mid+1;i<=r;i++)
    {
        sum+=a[i];
        if(sum>right_max)
            right_max=sum;
    }
    __int64 ans=left_max+right_max;
    if(leftmaxInterval>ans)
        ans=leftmaxInterval;
    if(rightmaxInterval>ans)
        ans=rightmaxInterval;
    return ans;
}
int main()
{
   int i,j,n;
   while(~scanf("%d",&n))
   {
        for(i=0;i<n;i++)
            scanf("%d",a+i);
        printf("%I64d\n",maxInterval(a,0,n-1));
   }
    return 0;
}

/* 3.动态规划法 计算时间复杂度为O(n),是最优的解

令b[j]表示以位置 j 为终点的所有子区间中和最大的一个

子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中

如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含

如果b[j-1]<=0,那么b[j] = a[j] ,因为既然最大,前面的负数必然不能使你更大

*/

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[50005];
__int64 b[50006];
int main()
{
   // freopen("1.txt","r",stdin);
    __int64 max;
    int i,n,flag;
    while(~scanf("%d",&n))
    {
        flag=0;
        max=0;
        for(i=1; i<=n; i++)
        {
            scanf("%d",a+i);
            if(a[i]>0)
                flag=1;
        }
        memset(b,0,n+1);
        for(i=1; i<=n; ++i)
        {
            if(b[i-1]>0)
                b[i]=b[i-1]+a[i];
            else
                b[i]=a[i];
            if(b[i]>max)
                max=b[i];
        }
        if(flag)
            printf("%I64d\n",max);
        else
            puts("0");
    }
    return 0;
}

 

目录
相关文章
|
8月前
【每日一题Day266】LC18四数之和 | 排序+双指针
【每日一题Day266】LC18四数之和 | 排序+双指针
47 1
|
8月前
【每日一题Day149】LC2389和有限的最长子序列 | 贪心+前缀和+二分查找
【每日一题Day149】LC2389和有限的最长子序列 | 贪心+前缀和+二分查找
50 0
|
8月前
【每日一题Day331】LC2560打家劫舍 IV | 二分查找 + 贪心
【每日一题Day331】LC2560打家劫舍 IV | 二分查找 + 贪心
49 0
|
8月前
|
搜索推荐 算法 索引
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
【排序算法】深入解析快速排序(霍尔法&&三指针法&&挖坑法&&优化随机选key&&中位数法&&小区间法&&非递归版本)
211 4
|
8月前
【每日一题Day306】LC228汇总区间 | 双指针
【每日一题Day306】LC228汇总区间 | 双指针
43 0
|
8月前
【每日一题Day261】LC16最接近的三数之和 | 双指针
【每日一题Day261】LC16最接近的三数之和 | 双指针
45 0
|
8月前
【每日一题Day328】LC198打家劫舍 | 动态规划
【每日一题Day328】LC198打家劫舍 | 动态规划
57 0
|
8月前
【每日一题Day260】LC15三数之和 | 排序 + 双指针
【每日一题Day260】LC15三数之和 | 排序 + 双指针
65 0
|
8月前
【每日一题Day122】LC1237找出给定方程的正整数解 | 双指针 二分查找
【每日一题Day122】LC1237找出给定方程的正整数解 | 双指针 二分查找
49 0
|
算法
poj 1961 Period(kmp最短循环节)
给定一个长度为n的字符串s,求他每个前缀的最短循环节。换句话说,对于每个i(2<=i<=n),求一个最大的整数k(如果k存在),使得s的前i个字符可以组成的前缀是某个字符串重复k次得到的。输出所有存在K的i和对应的k。
56 0