洛谷[P1115 最大子段和] {前缀和与差分}
题目描述
给出一个长度为 nn 的序列 aa,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 nn。
第二行有 nn 个整数,第 ii 个整数表示序列的第 i个数字 ai。
输出格式
输出一行一个整数表示答案。
输入输出样例
输入
7
2 -4 3 -1 2 -4 3
输出
4
说明/提示
样例 解释
选取 [3,5] 子段 {3,−1,2},其和为4。
数据规模与约定
对于 40% 的数据,保证 n≤
1
0
3
10^3
103 。
对于 100% 的数据,保证1≤n≤2×
1
0
5
10^5
105 ,−
1
0
4
10^4
104 ≤ai ≤
1
0
4
10^4
104。
解题思路
虽然是经典的dp,但是既然在前缀和的题单里面发现了它,多学一种方法也是很好的,以备不时之需。
分两部分进行考虑
首先,考虑长度为一,也就是只有数组元素自身。
之后,设置一个minn变量代表前i个数组元素的和的最小值。(注意:最初设置为min(0,a[1]),如果是a[1]是正数,那么就取0,如果是负数,那么就取其本身。)
最后,只要求取前缀和减去minn的最大值就可以,与此同时需要不断更新minn,保证其是前n个数和的最小值。这样既可以保证数据不断更新,还可以保证不出现超时的情况。
完整代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int a[maxn],b[maxn];
int minn=-1e9;//minn代表之前序列前缀和的最小值
int main()
{
int n;
scanf("%d",&n);
int ans=-1e9;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
ans=max(ans,a[i]);//如果只取一个数的时候就是最大值
b[i]=b[i-1]+a[i];//求前缀和
}
minn=min(0,a[1]);// 如果首位是正数那么就取0,如果首位是负数那么就取负数
for(int i=2;i<=n;i++)
{
ans=max(ans,b[i]-minn); //当前最大值与新的子序列和值作比较
minn=min(minn,b[i]); //获取前缀和的最小值
}
printf("%d",ans);
return 0;
}