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(dp[i-1]+a[i],a[i]);
這樣的話,問題是不是就明朗了呢?
int max = 0;
for(int i = 1; i <= n; i++)
容易看出來dp[i] 只和dp[i-1] 有關,因此記錄乙個last變數表示dp[i-1] 就能推出dp[i],所以並不需要開陣列的。
int last = 0,max = 0;
for(int i = 1; i <= n; i++)
若m不為1,那麼考慮給dp再加一維,表示段數。模擬一維的表述:
dp[i][j] 表示以a[j]結尾的i子段和的最優值。
考慮第j個元素:
若我們把a[j]接在在最後面一段裡,dp[i][j]可以表述為:x1 = dp[i][j-1]+a[j];
若我們讓a[j]自成一段,dp[i][j]可以表述為: x2 = max(dp[i-1][k])+a[j], 其中i-1 =< k <= j-1;
得到: dp[i][j] = max(x1,x2);
int ans = 0;
memset(dp,0,sizeof(dp);
for(int i = 1; i <= m; i++)
if(i == m) ans = max(ans,dp[i][j]);
}}
容易看出來 時間複雜度 o(m*n^2) 空間複雜度 o(m*n)
從上面容易看出來,其實在求dp[i][j] 時只用到了第i層與第i-1層,因此考慮用last陣列存上一層,dp存當前層減少空間開銷。
又注意到,其實第三層for迴圈其實是在求last陣列前幾項的最大值,因此可以直接處理好。
繼續優化:
ll ans = 0;
memset(dp,0,sizeof(dp));
memset(last,0,sizeof(last));
for(int i = 1; i <= m; i++)
for(int j = i; j <= n; j++)
}
容易看出來 時間複雜度 o(m*n) 空間複雜度 o(n)
#include using namespace std;
typedef long long ll;
const ll maxn = 5005;
const ll inf = 1e18;
ll dp[maxn],last[maxn];
ll a[maxn],b[maxn];
int main()
ll ans = 0;
memset(dp,0,sizeof(dp));
memset(last,0,sizeof(last));
for(int i = 1; i <= m; i++)
for(int j = i; j <= n; j++)
}cout << ans << endl;
return 0;
}
最大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子段和
最近掉入了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...
最大m子段和
最大m子段和 定義一串子段s1,s2,s3 sn 1,sn 求m段不交叉最大子段和 解 設dp i j 代表前j個數分成i段的最大和 包括a j 狀態轉移方程 dp i j max dp i j 1 a j dp i 1 t a j i 1 解釋兩種狀態的最優解 a j 恰好在下一次最優解末項之後,...