动态规划——最大子段和

一、最大子段和

问题

给定N个数A1, A2, ... An,从中选出k(k不固定)个连续的数字 Ai, Ai+1, ... Ai+k-1,使得∑i+k−1iAt 达到最大,求该最大值。

分析 
    求最大子段和可以用多种算法来解决. 
(1)直接枚举

max = 0;
for i in [1...n]
    for j in [i....n]
        sum = 0;
        for k in [i...j]
            sum += A[k]
        if(sum > max)
            max = sum
//时间复杂度为O(n^3)

(2)求 sum[i...j]时,直接利用 sum[i...j] = sum[i...j-1] + A[j]来优化

max = 0;
for i in [1...n]
    sum = 0
    for j in [i....n]
        sum += A[j]
        if(sum > max)
            max = sum
//时间复杂度为O(n^2)

(3)分治法 
    将A1...An用二分法分为左右两边,则A1...An中的最大连续子段和可能为三种情况: 
【1】是A1...An/2中的最大连续子段和 
【2】是An/2+1....An中的最大连续子段和 
【3】横跨 左右两边

int MaxSum(int* a, int beg, int end){
    if (beg == end){
        return a[beg] > 0? a[beg] :0;
    }
    int mid = (beg + end) / 2;
    int max_left = MaxSum(a, beg, mid);
    int max_right = MaxSum(a, mid + 1 ,end);
    int s1 = 0, s2 = 0, m_left = 0, m_right = 0;
    for(int i = mid; i <= beg; i --){
        s1 += a[i];
        if(s1 > m_left)
            m_left = s1;
    }
    for(int i = mid+1; i <= end; i ++){
        s2 += a[i];
        if(s2 > m_right)
            m_right = s2;
    }
    int max_sum = max_left;
    if(max_right > max_sum)
        max_sum = max_right;
    if(m_right + m_left > max_sum)
        max_sum = m_left + m_right;
    return max_sum;
}
//时间复杂度为 O(nlogn)

(4)动态规划算法 
    用动归数组 dp[i]表示以Ai结尾的若干个连续子段的和的最大值,则有递推公式: 
dp[i] = max{dp[i-1] + A[i], A[i]}

int max = 0;
for(int i = 1; i <= n; i ++){
   if(dp[i-1] > 0){
        dp[i] = dp[i-1] + A[i];
   }else{
        dp[i] = A[i];
   }
   if(dp[i]> max){
        max = dp[i];
   }
}
//时间复杂度为O(n)

二、最大子矩阵和

问题

给定MxN的矩阵,其子矩阵R{x1, y1, x2, y2} (x1, y1) 为矩阵左上角的坐标,(x2, y2)为矩阵右下角的坐标,S(x1,y1,x2,y2)表示子矩阵R中的数字的和,求所有子矩阵的和的最大值。

分析 
    矩阵有M行,line1, line2....lineM. 先限制行为从第i行到第j行,任意两列k和l,形成的一个子矩阵中元素的和: 由于行已经限定为第i行到第j行,则形成的子矩阵的和可以视为将第i行到第j行按照行相加得到一个一维数组T,选择T中k个元素到第l个元素之间的元素和。这又归为一个求最大子段和问题。 
    即先选定行范围(比如从第i行到第j行),求所选定行元素按列相加得到一维数组,再求该一维数组的最大子段和

int sum_line[M][M];
for(int i =1; i <= n; i ++){
    sum_line[1][i] = A[1][i];
}
for(int i = 2; i <= m; i ++){
    for(int k = 1;k <= n; k ++){
        sum_line[i][k] = sum_line[i-1][k] + A[i][k];
    }
}

for(int i = 1; i <= m; i ++){
    for(int j = i; j <= m; j ++){
        int b = 0;
        for(int k = 1; k <= n; k ++){
            if(b > 0){
                b += (sum_line[j][k] - sum_line[i-1][k]);
            }else{
                b = sum_line[j][k] - sum_line[i-1][k];
            }
            if(b > max){
                max = b;
            }
        }
    }
}
//时间复杂度为 O(m*m*n)

三、最大M子段和

问题

给定N个数,从中选出M个不重叠的连续子段,使得这些连续子段的和值最大,求该最大的和值。

分析 
    f[i][j]表示将A[1...j]这些数分成i个不相交的连续子段,且A[j]为第i个子段的末尾,这i个子段和的最大值。则有动归方程 f[i][j] = max{f[i][j-1] + A[j], max{f[i-1][k]} k = i-1, i+1...j - 1} 
    第一种情况是将A[1...j-1]分为i个不相交的连续子段,A[j-1]在第i个子段的末尾;此时再加上A[j]使得且A[j]和A[j-1]均位于第i个子段; 
    第二种情况是将A[1...k]分为i-1个不同的子段,A[k]位于第i-1个子段的末尾;此时加上A[j]使得A[j]单独成为第i个子段

辅助数组进行优化 
    f[i][j]表示将A[1...i]这些数分成j个不相交的连续子段(注意这里是将前i个数分成j个不相交的子段),且A[i]为第j个子段的末尾,这j个子段和的最大值;g[i][j]表示将A[1...i]这些数分成j个不相交的连续子段,且A[i]不一定为第j个子段的末尾,这j个子段和的最大值。 
则有递推关系: 
f[i][j] = max{f[i-1][j] + A[i], g[i-1][j-1] + A[i]} 
//对应两种情况:1. A[i]至少和前面的一个数位于第j个子段内;2. A[i] 自己位于第j个子段内

g[i][j] = max{g[i-1][j], f[i][j]} 
//对应两种情况:1. A[i]不在第j个子段内,这相当于将 A[1...i-1]划分为j个子段 2. A[i]位于第j个子段内

int max_sum(int m, int n){
    int i, j, t;
    for(i = 1; i <= n; i ++){
        t = min(i, m);
        for(j = 1; j <= t; j ++){
            f[j] = max(f[j], g[j-1]) + A[i];
            g[j-1] = max(g[j-1], f[j]);
        }
    }
    return g[m];
}
时间: 2024-10-13 10:47:50

动态规划——最大子段和的相关文章

动态规划--最大子段和

给定由n个整数(包含负整数)组成的序列a1,a2,...,an,求该序列子段和的最大值. 当所有整数均为负值时定义其最大子段和为0. 例如,当(a1,a2, ……a7,a8)=(1,-3, 7,8,-4,12, -10,6)时,最大子段和为:23 bj是1到j位置的最大子段和: a1 a2 … ai … aj … an-1 an |<-------bj-------------->| 由bj的定义易知,当bj-1>0时bj=bj-1+aj,否则bj=aj. 则计算bj的动态规划递归式:

算法重拾之路——最大子段和

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 第二章:动态规划 >最大子段和< 算法描述: ?给定由n个整数(可能为负整数)组成的序列 a1,a2, ... , an ,求该序列形如  从ai 到 aj (i ≤ j)的子段和的最大值.当所有整数均为负整数时定义其最大值为0.根据这个定义,所求的最优值为:

动态规划(普及组)

入门篇:动态规划思想 动态规划向来都是OI竞赛生涯中的分水岭. 开篇杂谈 文章中有任何地方不懂可联系我$qq:2832853025$,退役前全天在线. 前置技能 DFS搜索. 记忆化搜索. 递推式.(高中必修五数学) 个人理解 照搬定义肯定不是传授知识的好办法,呢只是老师PPT上面爱放的东西. 在我个人的理解中,动态规划只是搜索的一种优化方法,但是并不可以优化所有的搜索.一般的来说,符合下面三条情况的搜索是可以转化为动态规划的思想来做的. 重叠子问题. 最优子结构. 子问题无后效性. 通俗的借用

算法第三章上机实践

1.实践题目 最大子段和 2.问题描述 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],-,a[n],求该序列如a[i]+a[i+1]+-+a[j]的子段和的最大值.当所给的整数均为负数时,定义子段和为0. 要求算法的时间复杂度为O(n). 输入格式: 输入有两行: 第一行是n值(1<=n<=10000): 第二行是n个整数. 输出格式: 输出最大子段和. 3.算法描述 int maxsum(int a[],int n) { int sum=0,k=0; for(int i=

HDU 1024 Max Sum Plus Plus【动态规划求最大M子段和详解 】

Max Sum Plus Plus Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 29942    Accepted Submission(s): 10516 Problem Description Now I think you have got an AC in Ignatius.L's "Max Sum" problem

最大子段和 分治与动态规划

问题:  给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值.当所给的整均为负数时定义子段和为0,依此定义,所求的最优值为:    Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n    例如,当(a1,a2,a3,a4,a4,a6)=(-2,11,-4,13,-5,-2)时,最大子段和为20. 问题求解: 方法一:枚举 学过程序设计的都会,那就是枚举i和j,求i和a[i]

HDOJ-1003 Max Sum(最大连续子段 动态规划)

http://acm.hdu.edu.cn/showproblem.php?pid=1003 给出一个包含n个数字的序列{a1,a2,..,ai,..,an},-1000<=ai<=1000 求最大连续子段和及其起始位置和终止位置,很基础的动态规划(DP)问题,看完DP第一次做的DP题目 DP真的是一种很优美的算法,或者说思想,但是比较难理解,我对DP的理解还很浅薄 # include <stdio.h> # define INF 1000000000 int main() { i

最大m子段和问题 Max Sum Plus Plus —— 动态规划

"最大m子段和" 问题 Max Sum Plus Plus 问题描述: 给定由n个整数(可能为负数)组成的序列a1,a2,a3--an,以及一个正整数m,要求确定此序列的m个不相交子段的总和达到最大.最大子段和问题是最大m字段和问题当m=1时的特殊情形. OJ题目源地址: http://acm.hdu.edu.cn/showproblem.php?pid=1024 Max Sum Plus Plus Time Limit: 2000/1000 MS (Java/Others)    M

[ACM] POJ 2479 Maximum sum (动态规划求不相交的两段子段和的最大值)

Maximum sum Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 33363   Accepted: 10330 Description Given a set of n integers: A={a1, a2,..., an}, we define a function d(A) as below: Your task is to calculate d(A). Input The input consists o