关于求最大连续子序列和O(n)的一些思考

版权声明:未经博主同意,不可转载 https://blog.csdn.net/pythonbanana/article/details/88694046

首先,这个问题并不复杂,有O(n^2)的前缀和做法,还有分治O(nlgn)的做法。
还有O(n)的做法。
O(n)的做法,目前我知道了两种。
一种就是
sum[i,j] = sj - s(i-1),先计算前缀和,然后从左往右记录当前碰到的最小前缀和的下标,因为固定sj,就要使s(i-1)最小。然后在从左往右跑一遍,即可。
这种做法需要计算前缀和,和预处理相应的下标。总共多需要两个数组。
接下来的一种做法,节约了空间。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<stack>
#include<queue>
#include<map>
#include<cstring>
#include<string>
#include<cmath>

using namespace std;

typedef long long LL;

#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define pii pair<int,int>
#define all(x) x.begin(),x.end()
#define mem(a,b) memset(a,b,sizeof(a))
#define per(i,a,b) for(int i = a;i <= b;++i)
#define rep(i,a,b) for(int i = a;i >= b;--i)
const int maxn = 3e5;
int n = 0,m = 0;
int a[maxn+10];
int s[maxn+10];
int sm[maxn+10];//记录从1-j中最小的s的下标i,s[j] - s[i-1]最大,固定 s[j],相当于使s[i-1]最小
//然后扫描一次数组,维护目前遇到过的最小S
//复杂度O(n) 

int main(){
	while(~scanf("%d",&n)){
		int l = 0,r = 0;//记录所求区间的端点值 
		memset(s,0,sizeof(s));
		 
		per(i,1,n){
			scanf("%d",&a[i]);
			s[i] = s[i-1] + a[i];
		}
		int mins = 0,min_id = 0;
		sm[1] = 0;
		per(i,2,n){//录从1-i中最小的sj的下标j ,s[j] - s[i-1]最大,固定 
			if(s[i-1] < mins){//取不取等号,要根据实际情况来 
				mins = s[i-1];
				sm[i] = min_id = i-1;
			}else{
				sm[i] = min_id;
			}
		}
		int maxv = -INF;
		per(i,1,n){
			if(s[i] - s[sm[i]] > maxv){
				l = sm[i] + 1;r = i;
				maxv = s[i] - s[sm[i]];
			}
		}
		
		printf("%d %d : %d\n",l,r,maxv);
	}
	
	
	return 0;
}

2,设s[i]表示以a[i]结尾的连续子序列的最大和。由于递推s[i]数组可以节约为一个变量即可。

#include<bits/stdc++.h>
#define per(i,a,b) for(int i = (a);i <= (b);++i)
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 3e5;
int a[maxn+10]; 
int n = 0;
void solve(){
	int l = 1,r = 1;
	int s = 0,maxv = -INF;
	per(i,1,n){
		if(s < 0){
			s = a[i];
			if(s > maxv){
				maxv = s;
				l = i,r = i;
			}
		}else{
			s += a[i];
			if(s > maxv){
				maxv = s;
				r = i;
			}
		}
	}
	printf("The maximum is %d\nThe internal is %d to %d\n",maxv,l,r);
}
int main(){
	while(~scanf("%d",&n)){
		per(i,1,n){
			scanf("%d",&a[i]);
		}
		solve();
	}
	
	return 0;
} 

第二种为什么比第一种方法节约空间。
前者不是增量算法,后面的算法通过增量递推,充分利用了每一步的计算结果。增量,这种xx,在很多方面都有很好的应用,可以是算法变得高效。

猜你喜欢

转载自blog.csdn.net/pythonbanana/article/details/88694046