最大m子段和

最大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;

}

————————————————
版权声明:本文为CSDN博主「日月火山」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SunMoonVocano/article/details/83721171

 

posted @ 2023-10-08 21:46  菜鸡一枚  阅读(48)  评论(0)    收藏  举报