HDOJ 1024 M子段最大和問題

2021-05-31 22:05:26 字數 1833 閱讀 9351

問題:

給定由n個整數(可能為負整數)組成的序列e1,e2,…,en,以及乙個正整數m,要求確定序列的m個不相交子段,使這m個子段的總和達到最大。

分析:

設b(i,j)表示陣列e的前j項中i個子段和的最大值,且第i個子段含e[j](1£ i £m,i£ j £n)。以下稱b(i, j)為「最後乙個元素屬於第i子段的j元素i子段問題」。則n個元素中求i個子段的最優值顯然為:

best(i, n) = max (i <= j <= n)

計算b(i,j)的最優子結構為:

b(i,j) = max + e[j] } (i <= t < j)

這樣,可以得到時間複雜度為o(m * n ^ 2)和空間複雜度為o(m * n)的ms相當漂亮而且容易理解的dp演算法。當n不大的時候,這個演算法足夠優秀,然而,當n很大的時候,這個演算法顯然是不能讓人滿意的!

優化:

觀察上面的最優子結構,我們發現b(i, j)的計算只和b(i, j-1)和b(i-1, t)有關,也就是說只和最後乙個元素屬於第i子段的j-1元素i子段問題和前j-1個元素的最大i-1子

段問題有關(可以分別理解為將e[j]作為最後乙個元素而併入第i子段和將e[j]另起一段作為第i分段)。這樣,我們只要分別用curr_best和prev_best兩個一維陣列保

存當前階段和前一階段的狀態值b(i, *)和b(i-1, *) 就行了,記憶體使用也就可以降為o(2 * n)。

再來看看時間。分析發現,原演算法低效主要是在求max_sum(i, t) = max (i <= t < j)的時候用了o(n)的時間。其實,在求b(i, j)的過程中,我們完全

可以同時計算出max_sum(i, t),因為max_sum(i,j) = max,這個只花費o(1)的時間。而max_sum(i,t)不就是i+1階段中要用到的嗎?

關鍵問題已經解決了!那如何儲存max_sum呢?再開乙個陣列?我們可以在prev_best陣列中儲存!這個陣列的任務相當艱鉅,它既存放著i-1階段的max_sum數

值,又存放這供i+1階段使用的i階段的max_sum值。ms這有點矛盾?其實這是可行的。注意到我們在計算b(i,j)時只使用了prev_best[j-1],使用完了再也沒有用

了,這樣空閒著豈不浪費?其實我們可以將max_sum(i, j-1)存放到prev_best[j-1]裡面——這個主意相當不錯,它讓所有問題迎刃而解。

現在,我們得到了乙個時間複雜度為o(m * n)、空間複雜度為(2 * n)的演算法。這個演算法相當優秀,以至於m為小常數,n = 1000000時,結果也是瞬間就出來

了(此時演算法的時間複雜度可以認為是o(n)的)。

1

#include

<

stdio.h

>

2#include

<

stdlib.h

>

3#define

min_sum 0x8000000045

intmax_sum(

inte,

intn,

intm)630

prev_best[j -1

] =max_sum;31}

3233

free(prev_best);

34free(curr_best);

3536

return

max_sum;37}

3839

intmain()

4050

return0;

51}

最大m子段和

最大m子段和問題 給定由n個整數 可能為負 組成的序列a1 a2 a3.an,以及乙個正整數m,要求確定序列的m個不想交子段,使這m個子段的總和最大!設b i,j 表示陣列a的前j項中i個子段和的最大值,並且第i個子段包含a j 1 i m,i j n 則所求的最優值為maxb m,j m j n ...

最大m子段和

51nod 1052 題意描述 給定陣列a,長度為n。給定整數m,求不相交的m段字段和的最大值。當m 1 時 該問題就是最大子段和問題。設dp i 為以a i 結尾的最大子段和,當我們考慮dp i 的時候如果dp i 1 0那麼肯定把a i 接在後面最優,否則,取a i 最優。得到 dp i max...

最大M子段和

最近掉入了dp的深淵,還附加數學知識,爽哉。在此分析一道提交了17次的dp n個整數組成的序列a 1 a 2 a 3 a n 將這n個數劃分為互不相交的m個子段,並且這m個子段的和是最大的。如果m n個數中正數的個數,那麼輸出所有正數的和。例如 2 11 4 13 5 6 2,分為2段,11 4 1...