码迷,mamicode.com
首页 > 其他好文 > 详细

最大子段和

时间:2015-04-26 12:23:51      阅读:175      评论:0      收藏:0      [点我收藏+]

标签:

最大子段和

Description

给定由n(1<n<100)个整数(包含负整数)组成的序列a1,a2,...,an,要在这n个数中选取相邻的一段ai,ai+1,...aj(1≤i≤j≤n),使其和最大,输出该序列子段和的最大值。

Input

输入有多组测试用例

每组测试用例由2行组成,第一行一个整数n,接下来第二行有n个整数

若整数n为0,表示输入结束

Output

输出该序列子段和的最大值

Sample Input

8
1 -3 7 8 -4 12 -10 6
0

Sample Output

23

问题分析及解答

/*前置代码*/
#include<iostream>
#include<cstdio> using namespace std; const int maxn = 1005; int a[maxn], _max[maxn]; /*在此加入工作代码 eg: work() */ /*后置代码*/ int main() { //freopen("in.txt","r",stdin); while(cin>>n, n) { for(int i = 1; i <= n; i ++) { cin>>a[i];//在整个过程为方便操作,我们数组的下表从 “1” 开始。 } work(); //or work(1,n); cout<<m<<endl; } return 0; }

 

 我们很容易能够想到暴力的方法,利用三个循环嵌套。(O(n^3))

//1.暴力法
void work()
{
    for(int i = 1; i <= n; i++)
    {
        for(int j = i; j <= n; j++)
        {
            int curm = 0;//保存当前的和。
            for(int k = i; k <= j; k++)
            {
                curm += a[k];
                if(curm > m)
                {
                    m = curm;
                }
            }
        }
    }
}

观察发现,暴力法存在大量的重复计算,可以进行一定的优化。(O(n^2))
//2.优化代码
void work()
{
    for(int i = 1; i <= n; i++)
    {
        int curm = 0;
        for(int j = i; j <= n; j++)
        {
            curm += a[j];
            if(curm > m)
            {
                m = curm;
            }
        }
    }
}

 

 优化的效率任不是很高,我们可以考虑分治法。(O(nlog(n)))

 将a[1技术分享n]分成a[1技术分享n/2]和a[n/2+1技术分享n]两部分,会产生一下三种情况
 (1)a[1技术分享n]的最大子段和与a[1技术分享n/2]的最大子段和相同
 (2)a[1技术分享n]的最大子段和与a[n/2技术分享n]的最大子段和相同
 (3)a[1技术分享n]的最大子段和为ai+技术分享+aj,1<=i<=n/2,n/2+1<=j<=n

 我们的目的是求出上述三种情况中最大的一种。

//3.分治法
int work(int l, int r)//l: left r: right { if(l == r) m = a[l];// > 0 ? a[l] : 0; else { int c = (l + r) / 2;//c: center int lm = work(l,c);//lm: left sum int rm = work(c+1,r);//rm: right sum int clm = 0;//center‘s left sum int tlm = 0;//temp left sum for(int i = c; i >= l; i--) { tlm += a[i]; if(tlm > clm) clm = tlm; } int crm = 0;//center‘s right sum int trm = 0;//temp right sum for(int i = c + 1; i <= r; i++) { trm += a[i]; if(trm > crm) crm = trm; } int cm = clm + crm;//center sum m = cm < lm ? ( rm < lm ? lm : rm) : (cm < rm ? rm : cm); } return m; }

  尽管上述代码已经比暴力好了很多,但是由于递归调用过程存储空间分配所带来的时间浪费有时也是不可小觑的。

  我们介绍一种更高效的算法——动态规划(O(n)),过程如下:

  记 _max[i] 为a[0…i]中,包含a[i]的最大子段和,则有
  (1) _max[i] = s[i-1]+a[i], s[i-1] > 0,
  (2) _max[i] = a[i], s[i-1] <= 0;

  我们的目的是求max(_max) 

//4.动态规划
void work()
{
    m =a[1];
    _max[1] = a[1];
    for(int i = 2; i <=n; i++)
    {
        _max[i] = _max[i-1] > 0 ? _max[i - 1] + a[i] : a[i];
        if(m < _max[i])
            m = _max[i];
    }
}

  

最大子段和

标签:

原文地址:http://www.cnblogs.com/yqbeyond/p/4457517.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!