视频讲解:GESP202503 六级 环线-信息学奥赛GESP等级考试真题解析
一、原题
题目描述
小 A 喜欢坐地铁。地铁环线有 n 个车站,依次以 1,2,⋯,n 标号。车站 i (1≤i<n) 的下一个车站是车站 i+1。特殊地,车站 n 的下一个车站是车站 1。
小 A 会从某个车站出发,乘坐地铁环线到某个车站结束行程,这意味着小 A 至少会经过一个车站。小 A 不会经过一个车站多次。当小 A 乘坐地铁环线经过车站 i 时,小 A 会获得 ai 点快乐值。请你安排小 A 的行程,选择出发车站与结束车站,使得获得的快乐值总和最大。
输入格式
第一行,一个正整数 n,表示车站的数量。
第二行,n 个整数 ai,分别表示经过每个车站时获得的快乐值。
输出格式
一行,一个整数,表示小 A 能获得的最大快乐值。
输入输出样例
输入 #1
4
-1 2 3 0
输出 #1
5
输入 #2
5
-3 4 -5 1 3
输出 #2
5
说明/提示
对于 20% 的测试点,保证 1≤n≤200。
对于 40% 的测试点,保证 1≤n≤2000。
对于所有测试点,保证 1≤n≤2×10^5,−10^9≤ai≤10^9。
二、做题思路
1)填充数据

cpp
#include<bits/stdc++.h>
using namespace std;
const int m=200005;
long long a[m];
int main(){
//1)填充数据
//1.1)确定车站数量n
int n;cin>>n;
//1.2)填充n个车站的快乐值
for(int i=1;i<=n;i++) cin>>a[i];
return 0;
}
2)动态规划:求最大/小子段和



cpp
#include<bits/stdc++.h>
using namespace std;
const int m=200005;
long long a[m];
long long max_dp[m],min_dp[m];
long long max_sum,min_sum;
int main(){
//1)填充数据
//...
//2)动态规划:求最大/小子段和
//2.1)初始化dp
max_sum=min_sum=max_dp[1]=min_dp[1]=a[1];
//2.2)计算当前最优=最优(累加之前,不累加之前)
for(int i=2;i<=n;i++){
//计算当前最优
max_dp[i]=max(a[i]+max_dp[i-1],a[i]);
min_dp[i]=min(a[i]+min_dp[i-1],a[i]);
//比较找出所有最优中的最优
max_sum=max(max_dp[i],max_sum);
min_sum=min(min_dp[i],min_sum);
}
return 0;
}
3)计算总和 和 最大值

cpp
#include<bits/stdc++.h>
using namespace std;
const int m=200005;
long long a[m];
long long max_dp[m],min_dp[m];
long long max_sum,min_sum;
int main(){
//1)填充数据
//...
//2)动态规划:求最大/小子段和
//2.1)初始化dp
long long maxx=a[1],sum=a[1];
max_sum=min_sum=max_dp[1]=min_dp[1]=a[1];
//2.2)计算当前最优=最优(累加之前,不累加之前)
for(int i=2;i<=n;i++){
//计算当前最优
max_dp[i]=max(a[i]+max_dp[i-1],a[i]);
min_dp[i]=min(a[i]+min_dp[i-1],a[i]);
//比较找出所有最优中的最优
max_sum=max(max_dp[i],max_sum);
min_sum=min(min_dp[i],min_sum);
//3)计算总和 和 最大值
sum+=a[i];
maxx=max(maxx,a[i]);
}
return 0;
}
4)输出结果
cpp
#include<bits/stdc++.h>
using namespace std;
const int m=200005;
long long a[m];
long long max_dp[m],min_dp[m];
long long max_sum,min_sum;
int main(){
//1)填充数据
//...
//2)动态规划:求最大/小子段和
//....
//3)计算总和 和 最大值
//...
//4)输出结果
//4.1)全是负数 ,输出最大值
if(maxx<0) cout<<maxx;
//4.2)否输输出最优 max(最大子段和,总和-最小子段和)
else{
cout<<max(max_sum,sum-min_sum);
}
return 0;
}
三、答案
cpp
#include<bits/stdc++.h>
using namespace std;
const int m=200005;
long long a[m];
long long max_dp[m],min_dp[m];
long long max_sum,min_sum;
int main(){
//1)填充数据
//1.1)确定车站数量n
int n;cin>>n;
//1.2)填充n个车站的快乐值
for(int i=1;i<=n;i++) cin>>a[i];
//2)动态规划:求最大/小子段和
//2.1)初始化dp
long long maxx=a[1],sum=a[1];
max_sum=min_sum=max_dp[1]=min_dp[1]=a[1];
//2.2)计算当前最优=最优(累加之前,不累加之前)
for(int i=2;i<=n;i++){
//计算当前最优
max_dp[i]=max(a[i]+max_dp[i-1],a[i]);
min_dp[i]=min(a[i]+min_dp[i-1],a[i]);
//比较找出所有最优中的最优
max_sum=max(max_dp[i],max_sum);
min_sum=min(min_dp[i],min_sum);
//3)计算总和 和 最大值
sum+=a[i];
maxx=max(maxx,a[i]);
}
//4)输出结果
//4.1)全是负数 ,输出最大值
if(maxx<0) cout<<maxx;
//4.2)否输输出最优 max(最大子段和,总和-最小子段和)
else{
cout<<max(max_sum,sum-min_sum);
}
return 0;
}