题面
以一个长为n的数列,求连续子段的最大值。
思路1
直接枚举O(n^3)TLE稳稳的
#include<iostream>
using namespace std;
int n, a[50050], ans;
int main(){
cin>>n;
for(int i = 1; i <= n; i++)cin>>a[i];
for(int i = 1; i <= n; i++){//枚举起点
for(int j = i; j <= n; j++){//枚举终点
int t = 0;
for(int k = i; k <= j; k++){//区间统计
t += a[k];
}
ans = max(ans, t);//更新最值
}
}
cout<<ans<<"
";
return 0;
}
前缀和优化的枚举法,O(N^2),还是TLE
#include<iostream>
using namespace std;
int n, a[50050], ans;
int main(){
cin>>n;
for(int i = 1; i <= n; i++){ cin>>a[i]; a[i]+=a[i-1];}
for(int i = 1; i <= n; i++)//枚举起点
for(int j = i; j <= n; j++)//枚举终点
ans = max(ans, a[j]-a[i-1]);//更新最值
cout<<ans<<"
";
return 0;
}
思路2
分治。以中间元素为基准,向左求出以中间元素为尾的最大子段和,向右求出以中间元素为首的最大子段和,两部分相加即横跨左右两部分的最大子段的和,三者最大即为答案。复杂度O(nlogn),可以水过,记得不开longlong会WA。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int n, a[50050];
LL dfs(int l, int r){
if(l==r)return a[l]>0?a[l]:0;//所给整数均为负数时和为0。
else{
int m = l+r>>1;
LL ls = dfs(l,m), rs = dfs(m+1,r);//左右两段单独
//跨中间
LL lsum = 0, lmax = 0;
for(int i = m; i >= l; i--){
lsum += a[i];
lmax = max(lmax, lsum);
}
LL rsum = 0, rmax = 0;
for(int i = m+1; i <= r; i++){
rsum += a[i];
rmax = max(rmax, rsum);
}
return max(lmax+rmax,max(ls,rs));
}
}
int main(){
cin>>n;
for(int i = 1; i <= n; i++)cin>>a[i];
cout<<dfs(1,n)<<"
";
return 0;
}
思路3
DP(覆盖了所有状态),如果当前记录的子段的和为负数时,就要以下一个点为起点重新找子段了。复杂度O(n),AC稳稳的。
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
int n, a[50050];
int main(){
cin>>n;
for(int i = 1; i <= n; i++)cin>>a[i];
LL ans=0, t=0;
for(int i = 1; i <= n; i++){
if(t > 0)t += a[i];
else t = a[i];
ans = max(ans, t);
}
cout<<ans<<"
";
return 0;
}