Max Sum Plus Plus(基礎dp)
題目連結:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=87287#problem/A
題意:給定由 n個整數(可能為負整數)組成的序列a1,a2,a3,……,an,以及一個正整數 m,要求確定序列 a1,a2,a3,……,an的 m個不相交子段,
使這m個子段的總和達到最大,求出最大和。
思路:動態規劃的思想。
1.基本思路:首先,定義陣列num[n],dp[m][n].
num[n]用來儲存n個整陣列成的序列.
dp[i][j]用來表示由前 j項得到的含i個欄位的最大值,且最後一個欄位以num[j]項結尾。仔細想想,我們可以知道:
dp[i][j]=max(dp[i][j-1]+num[j],dp(i-1,t)+num[j]) 其中i-1<=t<=j-1.
(因為必須是以 num[j] 結尾的,所以num[j]一定屬於最後一個子段,即要麼自己獨立成一個子段,要麼與前邊以num[j-1]結尾的子段聯合)
所求的最後結果為 max( dp[m][j] ) 其中1<=j<=n.
但是,我們會發現,當n非常大時,這個演算法的時間複雜度和空間複雜度是非常高的,時間複雜度近似為O(m*n^2),
空間複雜度近似為O(m*n).因此,我們需要優化演算法來降低時間複雜度和空間複雜度.
2.優化演算法:
(1)節省時間
由基本思路,我們可以知道,dp[i][j]=max(dp[i][j-1]+num[j],dp(i-1,t)+num[j]),其中i-1<=t<=j-1.我們只要找到dp[i][j-1]
和dp[i-1][t]的最大值加上num[j]即為dp[i][j].所以,定義一個數組pre_max[n],用pre_max[j-1]來表示求解dp[i][j]時dp[i-1][t]
的最大值,則dp[i][j]=max(pre_max[j-1],dp[i][j-1])+num[j].
特別注意,pre_max[n]這個位置的儲存空間是始終用不到的,因此可以用來儲存其他數值,在接下來會用到。
在求解dp[i][j]的同時,我們可以計算出dp[i][t];i<=t<=j的最大值,這個最大值在計算dp[i+1][j+1]的時候需要作為pre_max[j]的
形式被使用,我們先把它存在pre_max[n]中。
你可能會問:為什麼不把它直接放在pre_max[j]中呢?因為你接下來需要計算dp[i][j+1]的值,需要用到pre_max[j]中原來的值,
如果你把它存在這裡,就會覆蓋掉計算dp[i][j+1]所需要的那個值。所以,先把它放在pre_max[n]中。
當我們計算完dp[i][j+1]之後,就會發現pre_max[j]中的值已經沒有用處了,我們可以把它更新為計算dp[i+1][j+1]所需要的那個值,
即之前放在pre_max[n]中的那個值,即執行pre_max[j]=pre_max[n].
這樣我們就節省了計算最大值時付出的時間代價。
(2)節省空間
通過時間的節省,我們突然間發現程式執行結束後pre_max[n]的值即為最後的結果,pre_max[n]陣列才是我們希望求解的,
dp[m][n]這個龐大的陣列已經不是那麼重要了,因此,我們現在用整型數tmp來代替dp[m][n],用來臨時儲存dp[i][j]的值,
作為求解pre_max[n]的中介。
這樣就節省了dp[i][j]佔用的極大的空間.
程式碼:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cctype>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-8)
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 100005;
int num[maxn];
int pre_max[maxn];
int main()
{
int n,m;
while(scanf("%d%d",&m,&n)!=EOF)
{
for(int i=1; i<=n; i++)
scanf("%d",&num[i]);
memset(pre_max,0,sizeof(pre_max));
for(int i=1; i<=m; i++)
{
int tmp=0;
for(int j=1; j<=i; j++)
tmp+=num[j];
pre_max[n]=tmp;
for(int k=i+1; k<=n; k++)
{
tmp=max(pre_max[k-1],tmp)+num[k];
pre_max[k-1]=pre_max[n];
pre_max[n]=max(pre_max[n],tmp);
}
}
printf("%d\n",pre_max[n]);
}
return 0;
}