最大子段和到最大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;
}

猜你喜欢

转载自blog.csdn.net/weixin_45606191/article/details/107828135