子段和问题

一、最大子段和问题:
描述:给定由N个整数(可能为负整数)组成的序列a1, a2, ..., an,求该序列形如Sigma{ak} (k=从i到j)的子段和的最大值。
本问题可以用枚举和分治来做,但是枚举的复杂度很高,而分治的思路比较复杂,不容易实现。所以只记录DP的方法。
记B[j]=Max{Sigma{ak} (k=从i到j)} (1<=i<=j<=N) 
则最大子段和为 Max{B[j]} (1<=j<=N)
当B[j-1]>0时,B[j]=B[j-1]+A[j],否则B[j]=A[j]。
由此得出动态转移方程 B[j]=Max{B[j-1]+A[j], A[j]} (1<=j<=N)
程序如下:
int MaxSum(int B[], int N)
//长度为N的B数组,返回的结果为最大子段和
{
    int res=-999999, i, s=0;
    for (i=1; i<=N; i++)
    {
        if (s>0) s+=B[i];
        else s=B[i];
        if (s>res) res=s;
    }
    return res;
}
注:若B数组为全负整数,则返回最大的那个负整数。
二、最大子矩阵和:
描述:给定友N行M列的整数矩阵A,求A的一个子矩阵,使这个子矩阵的和最大。
方法是先枚举i行和j行,满足1<=i<=j<=N,将i行到j行求和成为一个子序列,对这个子序列求最大子段和,结果就是这个矩阵的最大子矩阵和。
程序如下:
//num数组为生成的子序列,res的结果为最大子矩阵和
for (i=1; i<=N; i++)
{
    memset(num, 0, sizeof(num));
    for (j=i; j<=N; j++)
    {
        for (k=1; k<=M; k++) num[k]+=A[j][k];
        p=MaxSum(num, M);
        if (p>res) res=p;
    }
}
三、最大m子段和:
描述:给定由N个整数组成的序列以及一个整数m,要求确定序列A的m个不相交子段,使这m个子段的总和达到最大。
Dp[i][j]表示包含第i个数的前i个数划分为j个子段和的最大值。
动态转移方程:Dp[i][j]=Max{Dp[i-1][j]+A[i], Max{Dp[k][j-1]+A[j] (i-1<=k<j)}} (1<=i<=M, i<=j<=N)
Dp[i-1][j]+A[i]表示第j个子段中包含A[i],Max{Dp[k][j-1]+A[j] (i-1<=k<j)}表示第j个子段只有A[i](从A[i]开始为第j个子段)。
引入F[i][j]来优化转移方程,F[i][j]表示前i个数划分为j个子段的最大值(可以不包含A[j])
F[i][j]=Max{F[i-1][j], Dp[i][j]}
由此得到:
Dp[i][j]=Max{Dp[i-1][j], F[i-1][j-1]}+A[i]
程序如下:
//给定序列A[1..N],F[M]为最大m子段和
memset(Dp, 0x88, sizeof(Dp));
memset(F, 0x88, sizeof(F));
Dp[0]=F[0]=0;
for (int i=1; i<=N; i++)
    for (int j=Min(i, M); j>=1; j--)
        F[j]=Max(F[j], Dp[j-1]), F[j]+=A[i], Dp[j]=Max(Dp[j], F[j]);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值