數組中子數組之和最大問題

  問題描述:給定一個包含N個元素的數據A(A[0], A[1], A[2]...A[N-1]),這個數組天然有不少子數組,那麼,這些子數組之和的最大值是什麼?ios

  Example: 數組[1, -2, 3, 5, -3, 2],最大的子數據是[3, 5],和爲8。算法

  解法一:窮舉法數組

  最直接,也是最簡單的方法,窮舉子數組A[i:j]的和,找出一個最大的,算法的時間複雜度O(N2),算法的代碼以下:spa

#include <iostream>
#include <climits>

using namespace std;

int max_sub_sum(const int* A, int n)
{
    int max_sum = INT_MIN;

    for (int i = 0; i < n; i++)
    {
        int sub_sum = 0;
        for (int j = i; j < n; j++)
        {
            sub_sum += A[j];                //sum(i,j)
            if ( max_sum < sub_sum)
            {
                max_sum = sub_sum;
            }
        }
    }

    return max_sum;

}

int main()
{
    int A[] = {1, -2, 3, 5, -3, 2};
    int max_sum = max_sub_sum(A, sizeof(A) / sizeof(int));
    cout<<"max sub sum:"<<max_sum<<endl;
    system("pause");
    return 0;
}

  解法二:分治法code

  將數組分紅兩個子數組:A[0:n/2]和A[n/2 + 1, n]那麼最大的組數據之和,必然出如今如下三種狀況;blog

  1)最大子段與A[0:n/2]重合;遞歸

  2)最大子段與A[n/2 + 1, n]重合it

  3)最大子段誇過A[0:n/2]和A[n/2 + 1, n-1]兩段。io

  對於1)、2)兩種狀況能夠遞歸解決,而對於第三種狀況,咱們只要找到A[0:n/2]以n/2爲終點最大和,A[n/2 + 1, n-1]以n/2 + 1爲起點的最大和,兩種相加便可。class

算法的時間複雜度爲O(NlogN),代碼以下:

#include <iostream>
#include <climits>

using namespace std;

int max_sub_sum(int* A, int i, int j)
{
    if (i == j)
    {
        return A[i];
    }

    int mid = (i + j) / 2;
    int max_sum = max(max_sub_sum(A, i, mid), max_sub_sum(A, mid+1, j));
    
    int left_mid_max_sum = INT_MIN;
    int mid_sum = 0;
    for (int k =mid; k >= i; k--)
    {
        mid_sum += A[k];
        if (left_mid_max_sum < mid_sum)
        {
            left_mid_max_sum = mid_sum;
        }
    }

    int right_mid_max_sum = INT_MIN;
    mid_sum = 0;
    for (int k = mid+1; k <= j; k++)
    {
        mid_sum += A[k];
        if (right_mid_max_sum < mid_sum)
        {
            right_mid_max_sum = mid_sum;
        }

    }

    max_sum = max(max_sum, left_mid_max_sum + right_mid_max_sum);

    return max_sum;

}

int main()
{
    int A[] = {1, -2, 3, 5, -3, 2};
    int max_sum = max_sub_sum(A, 0, (sizeof(A) / sizeof(int)) -1);
    cout<<"max sub sum:"<<max_sum<<endl;
    system("pause");
    return 0;
}

  解法三:動態規劃

  動態規劃問題依賴於兩個基本因素:1)最優子結構;2)重疊結構。首先定義兩個量All(i)和Start(i),All(i)表示從A[i:n-1]數組中最大的子段和,Start(i)則表示瞭如下表i做爲起始位置的最大子段和,那麼該問題的最優的遞歸表達式:

  All(i) = max(All(i+1), start(i))

  這個公式表示,All(i)取:從i下標開始的字段數組start(i),從i+1開始的最大字段和All(i+1)之間的最大值, start(i)的定義以下:

  start(i) = max(A[i], A[i] + start(i+1))

該解法的代碼以下:

#include <iostream>
#include <climits>

using namespace std;

int max_sub_sum(int* A, int n)
{
    int all = A[n-1];
    int start = A[n -1];

    for (int i = n -2; i >= 0; i--)
    {
        start = max(A[i], A[i] + start);        //start(i) = max(A[i], start(i+1))
        all = max(all, start);
    }

    return all;
}

int main()
{
    int A[] = {1, -2, 3, 5, -3, 2};
    int max_sum = max_sub_sum(A, sizeof(A) / sizeof(int));
    cout<<"max sub sum:"<<max_sum<<endl;
    system("pause");
    return 0;
}

  最後一種方法是淳樸的迭代算法。

  迭代算法看起來簡單,可是這個問題但有着極其驚人的療效:1)用變量per_sum保存當前以i-1做爲結束下標的字段數組和,若是per_sum小於0(前面的部分起到了「負面」做用,須要捨棄),從下標i開始重新字段數組,於此同時,不斷比較每次字段數組的最大值,當迭代結束是,便會獲得字段數組的組大和。 

#include <iostream>
#include <climits>

using namespace std;

int max_sub_sum(int* A, int n)
{
    int max_sum = INT_MIN;
    int sum = 0;

    for (int i = 0; i < n; i++)
    {
        if (sum < 0)                //前面的元素提到了負面做用,須要捨棄
        {
            sum = A[i];
        }
        else
        {
            sum += A[i];            //前面的元素提到了正面做用,須要保留
        }
        if (sum > max_sum)
        {
            max_sum = sum;
        }
    }

    return max_sum;

}
int main()
{
    int A[] = {1, -2, 3, 5, -3, 2};
    int max_sum = max_sub_sum(A, sizeof(A) / sizeof(int));
    cout<<"max sub sum:"<<max_sum<<endl;
    system("pause");
    return 0;
}
相關文章
相關標籤/搜索