最大子段和

最大子段和 - 洛谷


解题思路:

(1)给出一段长度为n的序列,选出其中连续且非空的一段,要求和最大。首先第一个想到的就是暴力方法,枚举可能出现的段的长度,确定该长度的起点,然后从该起点出发,找到这段区间的和进行打擂台即可。时间复杂度为O(N^3),数据范围最小为2*10^3,那么基本凉凉...(0分)

#include<bits/stdc++.h>
using namespace std;
int a[20005];
int main()
{
	int n,max=0;
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	
	for(int i=1;i<=n;i++)//可能出现的区间段的长度为1-n 
	{
		for(int j=1;j<=n-i+1;j++)//确定此区间段的起点 
		{
			long long sum=0;
			for(int k=j;k<=j+i-1;k++)//累加从该起点的区间内的和 
			sum=sum+a[k];
			
			if(sum>max)//打擂台 
			max=sum;	
		}	
	}
	cout<<max;
	return 0;
}

(2)可不可以在上述的情况下优化呢?如果将三层循环优化为两层的话,对于某些数据是可以得到部分分数的。如果浪费了时间,试着用空间来平衡一下,所有的区间和都求了一遍,发现时间主要浪费在这里,如果把求过的某些数据利用数组存起来,应该是可以减少一些时间的。

        当区间段为1的时候,也就是各个元素为一组,可以将它们放入二维数组的第一行的第一列到第n列,当区间段为2的时候,也就是求所有连续的两个数的和可以将他们放入二维数组的第二行的第二列到第n列,以此类推...我们以给定的7个数字推算,结果如下图:     

        打完表后发现,其实没有必要全部求一遍,当知道了区间段为2的总和的时候,那么区间段为3的总和就是在此基础上加上该列原本的值,递推公式为sum[i][j]=sum[i-1][j-1]+a[1][j],这样的话,利用两层循环可以建立好表,最后遍历找最大值即可,时间复杂度为O(N^2),空间复杂度为T(N^2),空间还可以再优化为滚动数组T(N)。(40分)

#include<bits/stdc++.h>
using namespace std;
long long a[2010][2010];
int main()
{
	long long n,max=-100000;//定义最大值,有可能出现负数 
	cin>>n;
	for(int i=1;i<=n;i++)
	cin>>a[1][i];
	
	for(int i=2;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		a[i][j]=a[i-1][j-1]+a[1][j];
	}//递推打表 
	
	for(int i=1;i<=n;i++)//在表中找到最大值 
	{
		for(int j=i;j<=n;j++)
		{
			if(a[i][j]>max)
			max=a[i][j];
		}
	}
	cout<<max;
	return 0;
}

(3)对于100%的数据n<=2*10^5,那么两层循环也是超时的,只能往线性复杂度上优化。看到是区间段求和,想到利用前缀和数组来处理,前缀和数组是用来记录从头到第i项的和的,那么可不可以存储从第1项到第i项内区间元素和的较大值呢?

        举个形象的例子,如果把每一个元素都看成是一个发光的灯泡,元素值的大小也就是灯泡的亮度,那么连续的子段也就意味着这些灯泡是串联的,如果有灯泡比之前所有的灯泡串联起来的亮度更亮,那么选择再开一条线,如下图:

        首先第一个灯泡进来,亮度值为2,那么在第一条线路上(亮度值为2),第二个灯泡进来,串联起来后发现整条线路还没之前亮 (亮度值为-2)但是也得串联进来,如果不加进来的话,后续的灯泡无法串联到这条线路中,相当于出现断路,所以得加!

        那么当一个新的灯泡进来以后,发现比本条线路上所有串联的灯泡总亮度都亮的话,那么就开始重新开一条线路,因为一个灯泡就已经比之前的都亮了,以前的队伍也就没有必要存在了,后续的灯泡只加入新的线路即可。例如第三个灯泡亮度为3,比前两个灯泡亮度-2大,那么也就没有必要继续串联在第一条,因为,不管后面灯泡情况,以3为开头的线路是更优的!

        还有一个问题,当第三个灯泡是前三个里面最亮的并且单独开辟了一条线路,那么我们建立一个前缀和数组来维护记录,sum[i]=max(sum[i-1],a[i]),来选择更优的亮度值,这样后续的每一个sum[i]都是存储的最优值。

        最后,将前缀和数组sum进行打擂台取最大值即可,并不是越往后越亮,也可能出现在中间的某个阶段(也就是说后续的灯泡进来既开辟不了新的线路,串联进来后还让整条线路亮度值更低了)。时间复杂度为O(N)(100分)


#include<bits/stdc++.h>
using namespace std;
int a[200005],sum[200005];
int main()
{
	int n,ans=-99999999;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	
	sum[1]=a[1];
	for(int i=2;i<=n;i++)//串联并联决策更优值
	{
		sum[i]=max(sum[i-1]+a[i],a[i]);
	}
    
    for(int i=1;i<=n;i++)//打擂台取最大值
    ans=max(sum[i],ans);

	cout<<ans;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值