NYOJ 742 (多段子串和)

题目:给你一个序列 S1, S2, S3, S4 ... Sx, ... Sn (1 ≤ x ≤ n ≤ 1,000,000, -32768 ≤ Sx ≤ 32767). 我们定义
sum(i, j) = Si + ... + Sj (1 ≤ i ≤ j ≤ n).现在给你一个 m(8>m>0&&m<n)你的任务是计算
sum(i1, j1) + sum(i2, j2) + sum(i3, j3) + ... + sum(im, jm) ;我们规定他是不相交的。

请输出m段最大和,比如:m = 2,n = 6 ,{-1 4 -2 3 -2 4} 它的结果是 9;


要用到两个DP;

dp[i][j]表示分成i段,最后一段以j结尾的最大子串和。

dp2[i][j]表示分成 i段,到第j个数字的最大字串和(分法不一定包括的j,注意和上面区分)

答案就是dp2[m][n];

转移方程为dp[i][j] = max(dp[i][j - 1], dp2[i - 1][j  - 1]) + z[j];//两种 情况,要么是接在前一个后面,要么是单独作为一段的开头

dp2[i][j] = max(dp[i][j], dp2[i][j - 1]);//考虑最新的分法是否超过了最大值


考虑使用滚动数组,dp的话只使用当前层的前一个,所以完全没有问题。dp2的话使用了当前层的前一个和上一层的前一个,所以我们在更新当前层的时候不能马上覆盖到上一层的,因为在下一层时要用到。所以考虑使用一个变量来暂时保存当前层;

转移方程变为:dp[j] = max(dp[j - 1], dp2[j - 1]) + z[j];
dp2[j - 1] = ans;//上一层的用完才覆盖
ans = max(dp[j], ans);//暂时不覆盖当前层,下一层要用


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <queue>
#include <vector>
using namespace std;

#define MAXN 275005
#define MOD 1000000 

#define LL long long int

int T, n, m, x;
int z[1000005];
LL dp[1000005];
LL dp2[1000005];
LL ans;

int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d %d", &m, &n);
		for (int i = 1; i <= n; ++i)
		{
			scanf("%d", &z[i]);
		}
		memset(dp, 0, sizeof(dp));
		memset(dp2, 0, sizeof(dp2));
		dp[0] = dp2[0] = 0;
		for (int i = 1; i <= m; ++i)
		{
			ans = -0x7f7f7f7f;
			for (int j = i; j <= n; ++j)
			{ 
				dp[j] = max(dp[j - 1], dp2[j - 1]) + z[j];
				dp2[j - 1] = ans;
				ans = max(dp[j], ans);
			}
		}
		printf("%lld\n", ans);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值