让找连续子段的最大和
1.首先想到的就是暴力枚举(测试点全都过不了)(还是太菜了)
时间复杂度太大
思路:枚举字串首尾两端的,找出不同的字串逐一相加
代码:
简单的想一下,1中做了许多重复的计算
2,当子序列的起始位置不变,末尾移动的时候,nowsum只会依次加上一个新的数,所以简单枚举
一下字串首尾两端的位置,每次子序列末尾移动的时候,新序列的和就为nowsum=nowsum+data[j];
复杂度还是很大,过不了后三个测试点,贴一下
代码:
3,就是dp了,仔细研究这个题,可以发现
当nowsum<0时,无论后边的数是正是负加上nowsum都将小于它自己,所以nowsum<0时
可以看成从0开始处理,还有一种序列全为负数的情况要考虑下,状态转移方程:
maxsum=max( maxsum , max(nowsum+data[i],data[i]);(可以过了)
代码:
显然上边三个只有最后一个是有用的,接下来就是用前缀和去做啦(虽然本来就是前缀和题)
1,最简单的一个算法
sum记录前缀和,bigsum更新最大值,注意下数据范围
代码:
2,滚动数组,如果前缀和sum变成了负数,那么下一个数就不需要前边的数了,此时把sum置为0,再继续
累加
代码:94ms
3,分治算法,
假定有区间【l,r】,中间位置是mid,最大子段为【i,j】,那么,i和j一定符合下边的三种情况
(1),l ≤ i ≤ j ≤ mid
(2),i ≤ mid < j ≤ r
(3),mid < i ≤ j ≤ r
知道这个就好办啦,递归计算
代码:
1 #include<bits/stdc++.h> 2 #define long long ll 3 const int maxn=200010; 4 const int inf=-0x3f3f3f; 5 6 using namespace std; 7 int data[maxn]; 8 int solve(int l,int r) 9 { 10 if(l==r) 11 return data[l]; 12 int mid=(l+r)>>1; 13 int sum=0,suml=inf,sumr=inf; 14 for(int i=mid;i>=l;i--) 15 { 16 sum+=data[i]; 17 suml=max(sum,suml); 18 } 19 sum=0; 20 for(int i=mid+1;i<=r;i++) 21 { 22 sum+=data[i]; 23 sumr=max(sum,sumr); 24 } 25 return max(max(solve(l,mid),solve(mid+1,r)),suml+sumr); 26 } 27 int main() 28 { 29 int n; 30 cin>>n; 31 for(int i=1;i<=n;i++) 32 scanf("%d",&data[i]); 33 cout<<solve(1,n); 34 return 0; 35 }