最大子段和问题

FIrst:无限制最大子段和问题 时间复杂度:O(n)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
long long f[N];

int main(){
	int n;
	scanf("%d",&n);
	long long res=-0x3f3f3f3f3f3f3f3f;
	for(int i=1;i<=n;i++){
		long long x;
		scanf("%lld",&x);
		f[i]=max(f[i-1],(long long)0)+x;
		res=max(res,f[i]);
	}
	printf("%lld\n",res);
	
	return 0 - 0;
}

Second:子段长度不大于m的最大子段和问题 时间复杂度:O(n)

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m;
long long sum[N];

int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        long long x;
        scanf("%lld",&x);
        sum[i]=x+sum[i-1];
    }
    deque<long long> q;
    q.push_back(0);
    long long res=-0x3f3f3f3f3f3f3f3f;
    for(int i=1;i<=n;i++){
        while(q.size()&&i-q.front()>m) q.pop_front();
        res=max(res,sum[i]-sum[q.front()]);
        while(q.size()&&sum[q.back()]>=sum[i]) q.pop_back();
        q.push_back(i);
    }
    printf("%lld\n",res);
    
    return 0 - 0;
}

 Third:长度不小于m的最长子段和问题 时间复杂度:O(n)

#include<bits/stdC++.h>
using namespace std;
const int N=2e5+10;
int sum[N],f[N],g[N];

int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		f[i]=max(f[i-1],0)+x;
		sum[i]=sum[i-1]+x;
	}
	int res=-0x3f3f3f3f;
	for(int i=1;i+m-1<=n;i++){
		g[i]=max(f[i-1],0)+sum[i+m-1]-sum[i-1];
		res=max(g[i],res);
	}
	printf("%d\n",res);
	
	return 0 - 0;
}

Foruth:环状最大子段和问题 时间复杂度:O(n)

 1.将序列倍长,就可以做一遍长度不大于m的最大子段和

 2.先正着求一遍最大子段和,考虑子段是否过端点,过端点就取反,然后求一个最小子段和,不过端点就没有影响。

Fifth:环状最大两段子段和 时间复杂度:O(n) / O(n^2)

O(n^2) 考虑破环成链,枚举破坏的断点O(n),在链上求最大两段子段和;维护一个f1[i]为以a[i]结尾的最大子段和,一个f2[i]为以a[i]开头的最大子段和;(其实就是倒着求一遍)复杂度;

O(n) 对于答案可能有的所有情况,一种是有一段跨过了端点,另一种是没有跨过端点;所以可以先求一遍两段最大子段和,再对整个序列取反,再求一遍(这时其实就是求出两段的最小子段和,用总和减去后就是跨过端点的两段最大子段和),这时把两种情况取一个max

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int s[N],front[N],back[N];

int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&s[i]);
	memset(front,-0x3f,sizeof(front)); 
	memset(back,-0x3f,sizeof(back));
	int sum1=-2e9,sum2=-2e9,sum=0;
	for(int i=1;i<=n;i++) front[i]=max(front[i-1],0)+s[i];
	for(int i=1;i<=n;i++) front[i]=max(front[i-1],front[i]);
	for(int i=n;i>=1;i--) back[i]=max(back[i+1],0)+s[i];
	for(int i=n;i>=1;i--) back[i]=max(back[i+1],back[i]);
	for(int i=1;i<n;i++) sum1=max(sum1,front[i]+back[i+1]);
	int cnt=0;
	for(int i=1;i<=n;i++){
		if(s[i]>0) cnt++;
	}
	if(cnt<=1){
		printf("%d\n",sum1);
		return 0 - 0;
	}
	memset(front,-0x3f,sizeof(front));
	memset(back,-0x3f,sizeof(back));
	for(int i=1;i<=n;i++) sum+=s[i],s[i]=-s[i];
	for(int i=1;i<=n;i++) front[i]=max(front[i-1],0)+s[i];
	for(int i=1;i<=n;i++) front[i]=max(front[i-1],front[i]);
	for(int i=n;i>=1;i--) back[i]=max(back[i+1],0)+s[i];
	for(int i=1;i<n;i++) sum2=max(sum2,front[i]+back[i+1]);
	sum2=sum+sum2;
	int res=max(sum1,sum2);
	printf("%d\n",res);
	
	return 0 - 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值