NYOJ742 &HDU1024 - Max Sum Plus Plus(最大M子段和)

41 篇文章 0 订阅

题目链接 NYOJ742     HDU1024

【题意】求最大M子段和

【分析】dp[i][j]表示前j项中i个子段和的最大值,且第i个子段包含a[j]
(a[j]可能包含在第i子段;也可能第i子段只有a[j]一个元素,那么只有把a[j]加到每一个dp[i-1][k]后面找最大值)
所以状态转移方程:dp[i][j] = max(dp[i][j-1], dp[i-1][k])(i-1 <= k <= j-1)。

可以写出:

		for (int i = 1; i <= m; i++)
		{
			for (int j = 1; j <= n; j++)
			{
				dp[i][j] = dp[i][j-1];
				for (int k = i-1; k < j; k++)
					dp[i][j] = max(dp[i][j], dp[i-1][k]);
				dp[i][j] += a[j];
			}
		}



但是这样复杂度m*n^2肯定超时,于是要优化。

这里发现for (int k = i-1; k < j; k++) dp[i][j] = max(dp[i][j], dp[i-1][k]);这个算max{dp[i-1][k]}在前面已经算出来了,只要保存下来就直接可以花费O(1)调用了;所以可以用另外一个数组last[]来分别保存上一次每个i-1~k的最大值max{dp[i-1][]},在用一个变量maxn来辅助计算当前到j时的最大值。这样就可以写成如下O(N*M)算法了:

		for (int i = 1; i <= m; i++)
		{
			maxn = -INF;
			for (int j = i; j <= n; j++)
			{
				dp[i][j] = max(dp[i][j-1], last[j-1])+a[j];
				last[j-1] = maxn;
				maxn = max(maxn,dp[i][j]);
			}
		}


 

又发现dp[i][j]的第一维没有用,利用滚动数组思想可以把第一维直接去掉,最终变成如下AC代码:

【AC代码】104ms(NYOJ) 640ms(HDU)

#include <cstdio>
#include <cstring>
#define MAXN 1000010
#define INF 0x3f3f3f3f
#define max(a,b) (a>b?a:b)
int ch, ans, f, a[MAXN], dp[MAXN], last[MAXN];

int in()
{
	if((ch = getchar()) == '-') f = -1;
	else f = 1;
	while(ch < '0' || '9' < ch) ch = getchar();
	ans = ch-'0';
	while((ch = getchar()) >= '0' && '9' >= ch) ans = ans*10+ch-'0';
	return ans*f;
}
int main()
{
#ifdef SHY
	freopen("e:\\1.txt","r",stdin);
#endif
	int t;
	t = in();
	while(t--)
	{
		int m = in(), n = in(), maxn;
		for (int i = 1; i <= n; i++)
			a[i] = in();
		memset(dp,-0x3f,sizeof(dp));
		memset(last,0,sizeof(last));
		for (int i = 1; i <= m; i++)
		{
			maxn = -INF;
			for (int j = i; j <= n; j++)
			{
				dp[j] = max(dp[j-1], last[j-1])+a[j];
				last[j-1] = maxn;
				maxn = max(maxn,dp[j]);
			}
		}
		printf("%d\n", maxn);
	}
	return 0;
}        


 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值