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]);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章