最大子序列和问题

问题: 
给定一整数序列A1, A2,... An (可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大。 
例如:整数序列-2, 11, -4, 13, -5, 2, -5, -3, 12, -9的最大子序列的和为19。对于这个问题,最简单也是最容易想到的那就是穷举所有子序列的方法。利用三重循环,依次求出所有子序列的和然后取最大的那个。当然算法复杂度会达到O(n^3)。

int max_sub_sum1(int a[],int size)
{
    int maxSum = 0;
    for(int i = 0; i < size; i++ )
        for(int j = 1; j < size; j++ )
        {
             int thisSum = 0;
             for(int k = i; k <= j; k++ )
                 thisSum += a[k];
             if(thisSum > maxSum )
                maxSum = thisSum;
        }
    return maxSum;
}

这个算法很简单,i表示子序列起始下标,j表示子序列结束下标,遍历子序列的开头和结束下标,计算子序列的和,然后判断最大子序列。很明显的看出算法复杂度是O(n^3)。

显然这种方法不是最优的,下面给出一个算法复杂度为O(n)的线性算法实现,算法的来源于Programming Pearls一书。在给出线性算法之前,先来看一个对穷举算法进行优化的算法,它的算法复杂度为O(n^2)。其实这个算法只是对对穷举算法稍微做了一些修改:其实子序列的和我们并不需要每次都重新计算一遍。假设Sum(i, j)是A[i] ... A[j]的和,那么Sum(i, j+1) = Sum(i, j) + A[j+1]。利用这一个递推,我们就可以得到下面这个算法:

int max_sub_sum2(int a[],int size)
{
    int max = a[0];
    for(int i = 0; i < size; i++)
    {
        int v = 0;
        for(int j = i; j < size; j++)
        {
            v = v + a[j];   //Sum(i, j+1) = Sum(i, j) + A[j+1]
            if(v > max)  max = v;
        }
    }
    return max;
}

那怎样才能达到线性复杂度呢?这里运用动态规划的思想。先看一下源代码实现:

int max_sub_sum3(int a[], int size)
{
    int max = 0,temp_sum = 0;
    for(int i = 0; i < size; i++)
    {
        temp_sum += a[i];
        if(temp_sum > max)
            max = temp_sum;
        else if(temp_sum < 0)
            temp_sum = 0;
    }
    return max;
}

在这一遍扫描数组当中,从左到右记录当前子序列的和temp_sum,若这个和不断增加,那么最大子序列的和max也不断增加(不断更新max)。如果往前扫描中遇到负数,那么当前子序列的和将会减小。此时temp_sum 将会小于max,当然max也就不更新。如果temp_sum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时将temp_sum置为0。然后,temp_sum将从后面开始将这个子段进行分析,若有比当前max大的子段,继续更新max。这样一趟扫描结果也就出来了。 

分治法:

最大子序列和可能出现在三个地方:整个出现在输入数据的左半部分,整个出现在输入数据的右半部分,或者跨越输入数据的中部从而占据左右两个半部分。

/**
 * Recursive maximum contiguous subsequence sum algorithm.
 * Finds maximum sum in subarray spanning a[left..right].
 * Does not attempt to maintain actual best sequence.
 */
int maxSumRec( const vector<int> & a, int left, int right )
{
    if( left == right )  // Base case
        if( a[ left ] > 0 )
            return a[ left ];
        else
            return 0;
    int center = ( left + right ) / 2;
    int maxLeftSum  = maxSumRec( a, left, center );
    int maxRightSum = maxSumRec( a, center + 1, right );
    int maxLeftBorderSum = 0, leftBorderSum = 0;
    for( int i = center; i >= left; i-- )
    {
        leftBorderSum += a[ i ];
        if( leftBorderSum > maxLeftBorderSum )
            maxLeftBorderSum = leftBorderSum;
    }
    int maxRightBorderSum = 0, rightBorderSum = 0;
    for( int j = center + 1; j <= right; j++ )
    {
        rightBorderSum += a[ j ];
        if( rightBorderSum > maxRightBorderSum )
            maxRightBorderSum = rightBorderSum;
    }
    return max3( maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum );
}  

/**
 * Driver for divide-and-conquer maximum contiguous
 * subsequence sum algorithm.
 */
int maxSubSum3( const vector<int> & a )
{
    return maxSumRec( a, 0, a.size( ) - 1 );
}  

 

 

作者:阿凡卢

出处:http://www.cnblogs.com/luxiaoxun/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

http://www.cnblogs.com/luxiaoxun/archive/2012/08/05/2623806.html

时间: 2025-01-02 05:40:26

最大子序列和问题的相关文章

经典面试题:最长公共子序列

1.问题描述: 什么是最长公共子序列呢?好比一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则S 称为已知序列的最长公共子序列.     举个例子,如:有两条随机序列,如 1 3 4 5 5 ,and 2 4 5 5 7 6,则它们的最长公共子序列便是:4 5 5.     注意最长公共子串(Longest CommonSubstring)和最长公共子序列(LongestCommon Subsequence, LCS)的区别:子串(Substring)是串的一

动态规划之最长公共子序列

给定两个序列x和y,称z是x和y的公共子序列,如果z既是x的子序列,又是y的子序列:最长的公共子序列称作最长公共子序列LCS(longest common subsequence). 解题思路 (1)LCS的最优子结构 设zk是xm和yn的一个LCS,则,如果x和y的最后一个元素相同,则z中去掉最后一个元素之后zk-1仍为xm-1和yn-1的LCS. 如果xm!=yn,若zk!=xm,则z是xm-1和y的一个LCS,若zk!=yn,则z是xm和yn-1的LCS. (2)一个递归解 设c[i][j

PHP求最大子序列和的算法实现

复制代码 代码如下: <?php //作者:遥远的期待 //QQ:15624575 //算法分析:1.必须是整数序列.2.如果整个序列不全是负数,最大子序列的第一项必须是正数,否则最大子序列后面的数加起来再加上第一项的负数,其和肯定不是最大的:3.如果整个序列都是负数,那么最大子序列的和是0: //全负数序列很简单,不举例 $arr=array(4,-3,5,-2,-1,2,6,-2); function getmaxsum($arr){ $thissum=0; $maxsum=0; $star

UVa 10405:Longest Common Subsequence,最长公共子序列模板题

[链接] http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=114&page=show_problem&problem=1346 [原题] Problem C: Longest Common Subsequence Sequence 1: Sequence 2: Given two sequences of characters, print the length of

动态规划解最长公共子序列(LCS)问题 (附可打印LCS完整代码)

一.动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题.简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加. 为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法. 二.问题:求两字符序列的最长公共字符子序列(LCS) 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的

最长递增子序列(LIS)的O(N^2)与O(NlogN)算法分析

题目: 求一个一维数组arr[n]中的最长递增子序列的长度,如在序列1,5,8,3,6,7中,最长递增子序列长度为4 (即1,3,6,7). 由于LIS用O(NlogN)也能打印,O(N^2)的DP方法见最后. 从LIS的性质出发,要想得到一个更长的上升序列,该序列前面的数必须尽量的小. 对于原序列1,5,8,3,6,7来说,当子序列为1,5,8时,遇到3时,序列已经不能继续变长了.但是,我们可以通过替换,使"整个序列"看上去更小,从而有更大的机会去变长.这样,当替换5-3和替换8-6

算法研究:最大子序列求和问题的解决方案

The maximum subarray problem is the task of finding the contiguous subarray within a one-dimensional array of numbers (containing at least one positive number) which has the largest sum. For example, for the sequence of values ?2, 1, ?3, 4, ?1, 2, 1,

最大子序列和问题从O(N^3)到线性的算法

算法复杂度,从开始学习算法分析之后就一直在讨论着这个问题,很多人都认为,计算机相关人才只是"高级蓝领","技术民工",那为什么计算机的大牛们依然乐此不疲呢?我想,是因为他们发现了思考的乐趣. 有时候,稍加思考,你所做的事情就会变得格外的美妙,有时候,更简短的代码带来的却是更高的执行效率,生活,恰是需要这样的点睛之笔. 好了,前奏铺垫的有点长,下面进入正题,通过对大家所熟知的一道题的分析,最大子序列和的问题,让我们来初步感受一下编程的美丽: 题目是这样描述的,给定指定

算法系列(六)最长公共子序列(LCS)问题(连续子序列)的三种解法

最长公共子序列(LCS)问题有两种方式定义子序列,一种是子序列不要求不连续,一种是子序列 必须连续.上一章介绍了用两种算法解决子序列不要求连续的最终公共子序列问题,本章将介绍要求 子序列必须是连续的情况下如何用算法解决最长公共子序列问题. 仍以上一章的两个字符串 "abcdea"和"aebcda"为例,如果子序列不要求连续,其最长公共子序列为"abcda",如果子序列 要求是连续,则其最长公共子序列应为"bcd".在这种情况下

算法系列(五)最长公共子序列(LCS)问题(非连续子序列)的两种解法

最长公共子序列也称作最长公共子串,英文缩写是LCS(Longest Common Subsequence).其定义 是:一个序列S,如果分别是两个或多个已知序列的子序列,且是符合此条件的子序列中最长的,则称 S为已知序列的最长公共子序列. 关于子序列的定义通常有两种方式,一种是对子序列没有连 续的要求,其子序列的定义就是原序列中删除若干元素后得到的序列.另一种是对子序列有连续的要 求,其子序列的定义是原序列中连续出现的若干个元素组成的序列.求解子序列是非连续的最长公共 子序列问题是一个十分实用的