給定由n個整數(可能為負)組成的序列a1、a2、a3...,an,以及乙個正整數m,要求確定序列的m個不想交子段,使這m個子段的總和最大!
動態規劃,借助矩陣可以直觀的看到計算過程。定義二維陣列dp, dp[ i ][ j ],表示前 j 項所構成 i 子段的最大和,且必須包含著第j項,即以第j項結尾
然後是乙個遞推過程。
求dp[ i ][ j ],有兩種情況
1、dp[ i ][ j ] = dp[ i ] [ j-1 ] + a[ j ] ,即把第j項融合到第 j-1 項的子段中,子段數沒變
2、dp[ i ][ j ] = dp[ i-1 ] [ t ] + a[ j ],(i-1<= t < j )
把第 j 項作為單獨的乙個子段,然後找一下i-1個子段時,最大的和,然後加上a[ j ]
然後比較上面兩種情況,取大的。下面看圖,紅色數字為輸入的序列:
如圖,要求dp[ 3 ][ 6 ],只需比較他左邊的那個,和上面那一行圈起來的之中最大的數,
再加上a[ j ] 即為dp[ 3 ][ 6 ] 的值。
優化一下:
1、沿著第m行的最後乙個元素,往左上方向畫一條線,線右上方的元素是沒必要計算的
那麼dp[ i ][ j ] ,j++的時候,j的上限為 i + n - m 即可。還有左下角那一半矩陣,也是不用計算的,因為1個數字不可能分為2個子段
2、每確定乙個dp[ i ][ j ],只需用到本行和上一行,所以不用開維陣列也可以,省記憶體。
開兩個一維陣列,pre和dp,pre記錄上一行,dp記錄當前行3、再對上一行紅圈中的數字找最大值時,若用乙個迴圈來找,容易超時。
優化方法:在每次計算dp之前,同時記錄下j前面的最大元素。
時間複雜度大致為o(m*(n-m+1)),mn-m方通過,分析情況1和2,就能發現,從左上角走到第 m 行的最後乙個元素即可,找出第 m 行的最大值即為答案。
詳見例題。
1、51nod 10521052 最大m子段和
基準時間限制:2 秒 空間限制:131072 kb 分值: 80
難度:5級演算法題
n個整數組成的序列a[1],a[2],a[3],…,a[n],將這n個數劃分為
互不相交的m個子段,並且這m個子段的和是最大的。
如果m >= n個數中正數的個數,那麼輸出所有正數的和。
例如:-2 11
-4 13
-5 6
-2,分為2段,11 -4 13一段,6
一段,和為26。
input
第1行:2個數n和m,中間用空格分隔。n為整數的個數,m為劃分為多少段。(2 <= n , m <= 5000)output第2 - n+1行:n個整數 (-10^9 <= a[i] <= 10^9)
輸出這個最大和input示例7 2output示例-211
-413-56
-2
26
【ac**】:#include#includetypedef long long ll;
ll max(ll a,ll b)
int main()
return 0;
}
51Nod 1052 最大M子段和
n個整數組成的序列a 1 a 2 a 3 a n 將這n個數劃分為互不相交的m個子段,並且這m個子段的和是最大的。如果m n個數中正數的個數,那麼輸出所有正數的和。例如 2 11 4 13 5 6 2,分為2段,11 4 13一段,6一段,和為26。刷刷水有益身心健康。不過我還是沒有一眼看出來。考慮...
51Nod1052 最大M子段和
n個整數組成的序列a 1 a 2 a 3 a n 將這n個數劃分為互不相交的m個子段,並且這m個子段的和是最大的。如果m n個數中正數的個數,那麼輸出所有正數的和。例如 2 11 4 13 5 6 2,分為2段,11 4 13一段,6一段,和為26。input 第1行 2個數n和m,中間用空格分隔。...
51Nod 1052 最大M子段和
先將同符號的每一段存下來,並記錄前後段標號,將正數段總和記錄為tot 開乙個最小堆存每一段的abs值。每次從堆中取出乙個x 去掉這一段,並將左右的段與之合併再存入堆中,作為撤銷或更改操作。每次操作都會減少一段,直到段數 k就退出。include include include include def...