题目连接
- 该题是luogu试炼场的2-13:T2
题目大意
- n个数字,求子段中,最大的连续和;
题目分析
-
看题目第一反应是队列:
-
但因为不知道子段的长度,所以很难判断何时出队列;
解题思路1:贪心
- 设当前是 i ,则前面的“段”的和不能是负数;
- 所以只要前面的“段”的和不是负数, i 就可能加进去;
- 如果前面的“段”的和 是负数, i 就重新开一段。
贪心的代码:
比较短,理解是关键
//luogu1115:最大子段和
//贪心
#include<bits/stdc++.h>
using namespace std;
int n,x,s=0,ans;
int main()
{
scanf("%d",&n);
scanf("%d",&x);//第一个数字单独存储
s=x;
ans=x;
if(s<0) s=0;
for(int i=2;i<=n;i++)
{
scanf("%d",&x);
s+=x;//前缀和
if(ans<s) ans=s;//记录答案
if(s<0) s=0;//如果前面的段是负数,就不要了
}
printf("%d",ans);
return 0;
}
思路2:线段树式的递归
- 设符合要求的段是 [x,y] ;
- 这一段可能出现在:
- L <= x < y <= mid ;
- mid < x < y <= R ;
- L <= x < mid < y <= R ;
- 所以不断二分下去,回溯的时候求解。
- 注意:[x,y] 是连续的,所以一定要从mid向两边求和!
代码2:类似线段树的二分递归
- 把回溯的内容理解透,才是真正理解递归
//luogu1115:最大子段和
//利用类似线段树的思想
//用递归的来做
#include<bits/stdc++.h>
using namespace std;
const int mx=2e5+5,mi=-2e5+5;
int a[mx],n;
int dfs(int l,int r)
{
if(l==r) return a[l]; //最下层的位置
int mid=(l+r)/2,s1=mi,s2=mi;
int s=0;
for(int i=mid;i>=l;i--)//从中间向左:求出左边的最大连续和
{
s+=a[i];
s1=max(s1,s);
}
s=0;
for(int i=mid+1;i<=r;i++)//从中间向右:求出右边的最大连续和
{
s+=a[i];
s2=max(s2,s);
}
return max(max(dfs(l,mid),dfs(mid+1,r)),s1+s2);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
int ans=dfs(1,n);
printf("%d",ans);
return 0;
}