1. 程式人生 > >Max Sum Plus Plus(基礎dp)

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;
}