hdu 1024 Max Sum Plus Plus
阿新 • • 發佈:2018-12-14
題意
n個數取m個不重合的部分使和最大,求這個最大值。
思路
列舉比大小的思路肯定不可取,嘗試動態規劃。
動態規劃最自然的想法是:求n個數取若干部分和的最大值,子問題就是對n-1個數取若干部分和的最大值。
設a[n]為n個數的序列,dpp[n][m]為n個數取m部分的最大值,可以有兩種決策達到dpp[n][m]的狀態:
- 第n個數構成第m部分
- 第n個數不構成第m部分
所以狀態轉移方程,:
對於這個方程我們考慮實現,n需要一層遍歷,m需要一層遍歷,x需要一層遍歷,變成的的演算法,應該會超時。
嘗試優化第一個決策引入的x,嘗試消除x的影響。
設dp[n][m]為n個數取m部分的最大值,且第n個數構成第m部分,之所以這樣設是因為我觀察到中有這樣(dp[n][m])的部分。(有點類似最長上升子序列的狀態)
這樣消除了x的影響,變成了的演算法。回過頭在看最後公式的兩部分可以看成:
- 第n個數獨立構成第m部分的情況
- 第n個數和前面的數一同構成第m部分的情況
同時
由於狀態轉移方程只涉及相鄰項的關係,所以可以狀態壓縮。(注意順序)
這題動態規劃的邊界條件是試出來的,詳情看程式碼。
看到航電discuss的另一中寫法,把分成m部分看成階段。
程式碼
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL INF = -1e18;
int s[1000010];
LL dp[1000010];
LL dpp[1000010];
int main(){
int m, n;
while(~scanf("%d%d", &m, &n)){
dpp[0] = dp[0] = 0;
for(int i = 1; i <= m; ++i){
dp[i] = INF;
dpp[i] = INF;
}
for(int i = 1; i <= n; ++i){
scanf("%d", s + i);
for(int j = min(i, m); j > 0; --j){
dp[j] = max(dpp[j-1], dp[j]) + s[i];
dpp[j] = max(dpp[j], dp[j]);
}
}
printf("%I64d\n", dpp[m]);
}
return 0;
}
總結
- 第一次感覺數學是一個工具,並且是一個好工具。