洛谷 p1115 最大字段和(前缀和差分)

让找连续子段的最大和

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 }

 

 题目链接:https://www.luogu.com.cn/problem/P1115

posted on 2020-03-15 00:13  mmn  阅读(366)  评论(0)    收藏  举报