程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 編程之美2.14——求數組的子數組之和的最大值

編程之美2.14——求數組的子數組之和的最大值

編輯:C++入門知識

問題:
1. 一個由N個整數元素的一維數組,求其所有子數組中元素和的最大值。
2. 如果數組首尾相鄰,也就是允許子數組A[i],...,A[n-1],A[0],...,A[j]存在,求其所有子數組總元素和的最大值。

1. 解法:
我們使用動態規劃的思想可以在O(n)的時間內計算出子數組之和最大值。
動態規劃問題往往非常靈活,所以在做題的時候不知道如何編寫,其實它有很清晰的分析方法,按照這個方法就可以較容易的解決動態規劃的問題。具體參考IOI2000 張辰的論文《動態規劃的特點及其應用》
階段:在所有以元素k結尾的子數組中,選出元素和最大的子數組,k=1,2...n。
狀態:以元素k結尾的和最大的子數組是包含以元素k-1結尾的和最大的子數組還是就只有元素k這一個元素,即有這兩個可選狀態。
之所以選擇這樣的階段和狀態,因為這種選擇方式具有無後效性,即階段k+1(以元素k+1結尾的子數組)只會與階段k(以元素k結尾的子數組)發生關聯,而與其它階段無關。在得到以每個元素結尾的和最大的子數組之後,只要取其中最大值就是所有子數組中最大的子數組。
[cpp] 
#include <iostream> 
#include <algorithm> 
using namespace std; 
 
#define MAXN 1003 
int A[MAXN]; 
 
// 動態規劃思想,時間復雜度O(n) 
int Tail[MAXN]; 
 
int main() 

    int n, i, j, k; 
    cin >> n; 
    for (i=1; i<=n; i++) 
        cin >> A[i]; 
    // 計算以k結尾的子數組之和的最大值,即子數組包含第k個數 
    Tail[1] = A[1]; 
    for (k=2; k<=n; k++) // k個階段 
        Tail[k] = max(A[k],Tail[k-1]+A[k]); // 只有兩個狀態 
    // 因為和最大的子數組肯定以某個數結尾,所以取這n個子數組的最大值 
    // 就是和最大的子數組 
    int All = Tail[1]; 
    for (i=2; i<=n; i++) 
        All = max(All, Tail[i]); 
    cout << All; 

雖然這種標准的動態規劃方法時間復雜度已經最優了,但它仍要占用O(n)的空間,對於一般的動態規劃問題占用較多的空間是不可避免的,但這個問題較簡單,仍可以繼續優化。我們把All取最大值的操作放入Tail的計算循環中,如下:
Tail[1] = A[1];
All = Tail[1];
for (k=2; k<=n; k++)// k個階段
{
Tail[k] = max(A[k],Tail[k-1]+A[k]);// 只有兩個狀態
All = max(All, Tail[k]);
}
由於循環體中只關心當前的Tail[k]和上一個Tail[k-1],可以省去之前所計算出的Tail[1],Tail[2]...Tail[k-2]的數據,如下:
Tail = A[1];
All = Tail;
for (k=2; k<=n; k++)// k個階段
{
Tail = max(A[k],Tail+A[k]);// 只有兩個狀態
All = max(All, Tail);
}
最後優化後的代碼就是書中最後給出的結果。

2. 解法:
把問題分為兩種情況:
(1)最大和子數組沒有跨過A[n]到A[1](如問題1)
(2)最大和子數組跨過A[n]到A[1]
對於情況(2),這樣的最大和子數組包含兩個部分:以A[1]開始的最大和子數組,以及以A[n]結尾的最大和子數組,並且這兩個子數組不允許重疊,那麼將這兩個子數組拼合起來就是情況(2)的解。
[cpp]
#include <iostream> 
#include <algorithm> 
using namespace std; 
 
#define MAXN 1003 
int A[MAXN]; 
 
int main() 

    int n, i, j, k; 
    cin >> n; 
    for (i=1; i<=n; i++) 
        cin >> A[i]; 
    // 和最大的子數組沒有跨過A[n]和A[1] 
    int Tail = A[1]; 
    int All = Tail; 
    for (k=2; k<=n; k++) // k個階段 
    { 
        Tail = max(A[k],Tail+A[k]); // 只有兩個狀態 
        All = max(All, Tail); 
    } 
    // 和最大的子數組跨過了A[n]和A[1] 
    int Start = A[1]; 
    for (i=2; i<=n && Start+A[i]>Start; i++) 
            Start += A[i]; 
    Tail = A[n]; 
    for (j=n-1; j>=1 && Tail+A[j]>Tail; j--) 
            Tail += A[j]; 
    if (i<j && Start+Tail > All) 
        All = Start+Tail; 
    cout << All; 

 作者:linyunzju

 


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved