最大m子段和

 

最大M子段和

 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] = max(dp[i-1]+a[i],a[i]);

    if(dp[i] > MAX) MAX = dp[i];

}

容易看出來dp[i] 只和dp[i-1] 有關,因此記錄一個last變量表示dp[i-1] 就能推出dp[i],所以並不需要開數組的。

int last = 0,MAX = 0;

for(int i = 1; i <= n; i++)

{

    last = max(last+a[i],a[i]);

    if(last > MAX) MAX = dp[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++)

{

    for(int j = i; j <= n; j++)

    {

        dp[i][j] = dp[i][j-1] + a[j];

        for(int k = j-1; k >= i-1; k--)

        {

           dp[i][j] = max(dp[i][j],dp[i-1][k]+a[j]);

        }

        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++)

        {

            dp[j] = dp[j-1] + a[j];

            dp[j] = max(dp[j],last[j-1]+a[j]);

            if(i == m) ans = max(ans,dp[j]);

        }

        for(int j = i; j <= n; j++)

        {

            last[j] = max(last[j-1],dp[j]);

        }

}

容易看出來 時間複雜度 O(m*n) 空間複雜度 O(n)

#include <bits/stdc++.h>

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()

{

int n,m;

scanf("%d%d",&n,&m);

for(int i = 1; i <= n; i++)

    {

        scanf("%lld",&a[i]);

    }

    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++)

        {

            dp[j] = dp[j-1] + a[j];

            dp[j] = max(dp[j],last[j-1]+a[j]);

            if(i == m) ans = max(ans,dp[j]);

        }

        for(int j = i; j <= n; j++)

        {

            last[j] = max(last[j-1],dp[j]);

        }

    }

    cout << ans << endl;

    return 0;

}

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章