HRBUST - 1684最大连续和

今天做了一道连续和的题,并且把相关的知识串了一下。

题目

在这里插入图片描述
这道题目是求下标,与我们往常见到的连续和不太一样,所以这道用普通的方法就可以解出。
首先需要知道的一点是用快读的话读取不了文件的数据。

关于快读

首先介绍一下快读,getchar()相对于scanf(),cin这些读入是最快的,快读也是利用这个特点,但一般我们是遇到有很大数据读入量的时候,就是出题人要卡读入数据的时候,这时候快读的优势就出来了,这里用到的快读是简单版,还有其他可以读入浮点型等…但一般这个普通快读就够用了。

关于题目思路

看题目,要求的是求出下标,注意也有要求:如果有多组满足条件的x和y,x 应尽量小。如果还有多解y也应尽量小。这里我们就可以维护几个变量,包括答案,还有记录当前最大前缀和,和左下标temp,用sum表示前缀和。循环一次,每次循环让sum加上当前a[i],然后进行判断,如果前缀和比当前最大前缀和大的话,就更新左右下标还有当前最大前缀和;再进行另外的一个判断,如果sum<=0的话,就重置sum=0,并更新temp。

//HRBUST - 1684
#include<cstdio>
#include<cstring>
//# define LOCAL //用快读读不了文件 
using namespace std;
typedef long long ll;
const int maxn = 50005;
ll a[maxn];
inline int read(){
	char ch=getchar();int x = 0,f = 1;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int main(){
#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif
	int t = read();
	while(t--){
		memset(a,0,sizeof(a));
		int n=read(),m=read();
		for(int i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		while(m--){
			int p=read(),q=read();
			ll sum = 0;
			ll m = -0x3f3f3f3f;//记录上一个最大前缀和 
			int x = p,y = q,temp = p;
			for(int i=p;i<=q;i++){
				sum += a[i];
				if(sum > m){
					m = sum;
					x = temp;
					y = i;
				}
				if(sum <= 0){
					sum = 0;
					temp = i+1;
				}
			}
			printf("%d %d\n",x,y);	
		}
	}
	return 0;
}

题外话1

我看到这道题的第一反应是想到的是求前缀和,也算是熟悉一下这个复杂度只有O(nlogn)求最大前缀和的算法,如下:

ll maxsum(int x,int y){
	if(y-x==1)	return a[x];
	int m = x+(y-x)/2;
	ll maxs = max(maxsum(x,m),maxsum(m,y));
	ll v,L,R;
	v = 0,L = a[m-1];
	for(int i=m-1;i>=x;i--)
		L = max(L,v+=a[i]);
	v = 0,R = a[m];
	for(int i=m;i<y;i++)
		R = max(R,v+=a[i]);
	return max(maxs,L+R);
}

这代码用到了“赋值运算本身具有返回值”的特点,一定程度上简化了代码,而不会牺牲可读性。
关于算法,这里用到了分治法

  • 划分问题:把问题的实例划分成子问题
  • 递归求解:递归解决子问题
  • 合并问题:合并子问题的解得到原问题的解

在上面代码中,其实算是二分的思想,就是对区间不断地划分两块,最后只有一个元素的返回,分别对两边的区间求其的最大连续和,然后最后与总区间的最大连续和进行比较取最大。在这里用到二分的一个技巧,取分界点的时候是用m = x + (y - x)/2;用来确保分界点总是靠近区间的起点。

题外话2

当然还有O(N^2)复杂度的算法;
就是利用递推求得前缀和,再用二重循环更新前缀和的最大值:

s[0] = 0;
int best = 0;
for(int i=1;i<=n;i++)
	s[i] = s[i-1] + a[i];
for(int i  = 1;i <= n;i++)
	for(int j = i;j <= n;j++)
		best = max(best,s[j]-s[i-1]);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章