最大子段和到最大m子段和(二维到一维)到最大3子段和(一维)(待改进)

一、最简单的最大子段和
状态转移方程:dp[i]=max(dp[i-1]+num[i],num[i]);

小延伸:以前写的博客
1、记录最大字段和的首尾位置
2、二维的最大字段和问题——最大子矩阵问题

二、最大m子段和 讲解

对于一段序列 S来说,我们可以从S中选出 互不相交的m个子段,使得这m个子段,使得m个子段 和最大,m的大小不限。

1、用一个dp[i][j]数组记录,前i个数字分成j段所能产生的最大j子段和,前提必须以i结尾。

2、这个前提的要求,提供了第i个数字的可能情况:
(1)第i个数字单独成为一段,与之前的 j-1段 组合。 (2)第i个数字与第i-1个数字融合成为第j段。

3、状态转移方程即为:dp[i][j]=max( dp[i-1] [j] , max( dp[t] [j-1] ) (j-1=<t<i)) + num[i]。

4、空间复杂度的优化:因为我们推到分成j段只需要 考虑 分成j-1段的情况,所以我们不用开 m * n的数组,只需要开 2 *n的数组即可。

三、最大3子段和

如果是最大2子段和,弄个前后缀和两个一维数组即可。而3字段在这基础上也可以做。

有些题目还具体规定3字段的各个区间长度,下面这题就是这种大杂烩:

题目:给定 n个整数(3≤n≤100000)排成一行,每个数字表示Alice看过的第 i 部电视剧的时长,现在规定每部电视剧的鸡汤值为电视时长的最小质因子。现在要求 Alice任意选出三段连续且不相交的区间,且区间长度分别是 a,b,c(a,b,c都是正整数,且 a,b,c的和不超过 n。且T<20,3≤n≤100000),使得选出的三个区间中电视剧的鸡汤值之和最大。

#include <bits/stdc++.h>
using namespace std;
long long sum[100005],resum[100005],val[100005];
int turn[10],len[10];
long long dp[1000005],fp[1000005];
long long ans=0;
int n,t,tot;
bool vist[5];
int prime[1000005],f[1000005];
bool vis[1000005];
void pre()
{
	for(int i=2;i<=1000000;i++)
	{
		if(!vis[i]) prime[++tot]=i,f[i]=i;
		for(int j=1;j<=tot && prime[j]*i<=1000000;j++)
		{
			vis[prime[j]*i]=1;
			f[prime[j]*i]=min(f[prime[j]],f[i]);
			if(i%prime[j]==0) break;
		}
	}
}
void work()
{
	int a,b,c;
	a=len[turn[1]];
	b=len[turn[2]];
	c=len[turn[3]];
	for(int i=a;i<=n;i++)
		dp[i]=max(dp[i-1],sum[i]-sum[i-a]);
	for(int i=n-c+1;i>=1;i--)
		fp[i]=max(fp[i+1],resum[i]-resum[i+c]);
	for(int i=a+1;i<=n-b-c+1;i++)
		ans=max(ans,dp[i-1]+sum[i+b-1]-sum[i-1]+fp[i+b]);
	return;
}
void dfs(int dep)
{
	if(dep==4)
	{
		work();
		return;
	}
	for(int i=1;i<=3;i++)
	{
		if(!vist[i])
		{
			turn[dep]=i;
			vist[i]=1;
			dfs(dep+1);
			vist[i]=0;	
		}
	}
}
int main()
{
	scanf("%d",&t);
	pre();
	while(t--)
	{
		ans=0;
		sum[0]=0;
		scanf("%d",&n);
		scanf("%d%d%d",&len[1],&len[2],&len[3]);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&val[i]);
			sum[i]=sum[i-1]+f[val[i]];
		}
		for(int i=n;i>=1;i--)
			resum[i]=resum[i+1]+f[val[i]];
		dfs(1);
		printf("%lld\n",ans);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最大子段和动态规划的推广可以通过以下两种方式进行: 1. 最大子段和多维数组:最大子段和问题通常是应用在一维数组中,但是也可以推广到多维数组中。例如,我们可以将二维数组看作一个矩阵,然后将最大子段和问题扩展为寻找矩阵中的最大子矩阵和。 对于多维数组,我们可以使用类似的动态规划思想解决问题。定义一个辅助数组dp,其中dp[i][j]表示以元素[i][j]为右下角的子矩阵的最大和。然后,我们可以通过以下递推关系计算dp[i][j]: dp[i][j] = max(dp[i-1][j] + dp[i][j-1] - dp[i-1][j-1], 0) + matrix[i][j] 其中,matrix[i][j]表示原始矩阵的元素值。最后,我们只需要遍历dp数组,找到其中的最大值即可得到最大子矩阵和。 2. 最大子段和和其他问题的结合:最大子段和问题可以与其他问题结合,形成更复杂的动态规划解法。例如,可以将最大子段和问题与最长递增子序列问题结合,求解在一个序列中既要满足递增条件,又要求和最大的子序列。 这种结合的方法需要根据具体问题进行调整,但基本思路是类似的:使用动态规划来构建辅助数组,然后通过递推关系计算最优解。最终,可以通过遍历辅助数组或者记录状态转移路径的方式,得到最优解。 总之,最大子段和问题的动态规划可以通过推广到多维数组及与其他问题结合的方式来应用于更广泛的场景中。具体的解法需要根据具体情况进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值