最大子段和——分治與動態規劃

問題:

  給定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。
      

 問題求解:

 
/*簡單算法:
**v[0]不保存數據
**T(n)=O(n^2).
*/
int MaxSum(int *v,int n,int *besti,int *bestj)
{
    int sum=0;
    int i,j;
    for (i=1;i<=n;i++)
    {
        int thissum=0;
        for (j=i;j<=n;j++)
        {
            thissum+=v[j];
            if (thissum>sum)
            {
                sum=thissum;
                *besti=i;
                *bestj=j;
            }
        }
    }
    return sum;
}
/*分治法:
**將a[1n]分成a[1n/2]和a[n/2+1n],則a[1n]的最大字段和有三種情況:
**(1)a[1n]的最大子段和與a[1n/2]的最大子段和相同
**(2)a[1n]的最大子段和與a[n/2n]的最大子段和相同
**(3)a[1n]的最大子段和爲ai++aj,1<=i<=n/2,n/2+1<=j<=n
**T(n)=2T(n/2)+O(n)
**T(n)=O(nlogn)
*/
int MaxSum_DIV(int *v,int l,int r)
{
    int k,sum=0;
    if(l==r)
        return v[l]>=0?v[l]:0;
    else
    {
        int center=(l+r)/2;
        int lsum=MaxSum_DIV(v,l,center);
        int rsum=MaxSum_DIV(v,center+1,r);

        int s1=0;
        int lefts=0;
        for (k=center;k>=l;k--)
        {
            lefts+=v[k];
            if(lefts>s1)
                s1=lefts;
        }

        int s2=0;
        int rights=0;
        for (k=center+1;k<=r;k++)
        {
            rights+=v[k];
            if(rights>s2)
                s2=rights;
        }
        sum=s1+s2;
        if(sum<lsum)
            sum=lsum;
        if(sum<rsum)
            sum=rsum;
    }
    return sum;
}
/*動態規劃算法:
用b[j]表示以j結尾的序列對應的最大值。**b[j]=max{a[i]++a[j]},1<=i<=j,且1<=j<=n,則所求的最大子段和爲max b[j],1<=j<=n。
**由b[j]的定義可易知,當b[j-1]>0時b[j]=b[j-1]+a[j],否則b[j]=a[j]。故b[j]的動態規劃遞歸式爲:
**b[j]=max(b[j-1]+a[j],a[j]),1<=j<=n。
**T(n)=O(n)
*/
int MaxSum_DYN(int *v,int n)
{
    int sum=0,b=0;
    int i;
    for (i=1;i<=n;i++)
    {
        if(b>0)
            b+=v[i];
        else
            b=v[i];
        if(b>sum)
            sum=b;
    }
    return sum;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章