第一思路比较容易想到的是使用暴力算法解决,即直接枚举所有可能的子段,然后得到最大的字段和即可(用前缀和计算和)
//暴力 #include<iostream> #include<climits> #include<algorithm> using namespace std; const int MAX=2e5+10; int n,ans=INT_MIN; int arr[MAX]; int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>arr[i]; arr[i]=arr[i-1]+arr[i]; } for(int i=1;i<=n;i++){ for(int j=0;j<=i-1;j++){ ans=max(ans,arr[i]-arr[j]); } } cout<<ans<<endl; return 0; }
当然,该题的数据范围不会容许O(n^2)的暴力算法通过,因此我们需要更好的算法
这里介绍使用分治算法进行解答
思路分析:
主要的思路还是使用分治的思想,即将数组一次又一次地分成两个数组来求最大子段和,而最小的情况就是当分成只剩1个元素时,最大子段和就是该元素本身,此外,往上走一层,当分成只剩两个元素时,我们发现可以得到左右两边的最大子数组都可以得到,因此比较取最大值即可,是不是这样就好了呢??
例如当剩下两个元素1,1 此时左右最大子段和分别是1和1,但是实际上应该是2,也就是说,我们实际上忽略了一种情况:最大子段和在中间的情况,因此我们还需要计算中间的子段和与左右子段和比较取最大值即可
#include<iostream> #include<climits> using namespace std; const int MAX=2e5+10; int n; int arr[MAX]; //7 //2 -4 3 -1 2 -4 3 int getSubMAX(int i,int j){//分别代表左右下标 int sum; if(i==j){ return arr[i]; }else{ int center=(i+j)/2; //取左边最大子数组 int leftMAX=getSubMAX(i,center); //取右边最大子数组 int rightMAX=getSubMAX(center+1,j); //取中间最大子数组 int sum1=0,s1=INT_MIN; for(int k=center;k>=i;k--){ sum1+=arr[k]; if(sum1>s1){ s1=sum1; } } int sum2=0,s2=INT_MIN; for(int k=center+1;k<=j;k++){ sum2+=arr[k]; if(sum2>s2){ s2=sum2; } } sum=s1+s2; if(leftMAX>sum){//三者取最大 sum=leftMAX; } if(rightMAX>sum){ sum=rightMAX; } } return sum; } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>arr[i]; } int ans=getSubMAX(1,n); cout<<ans<<endl; return 0; }