【题解】【洛谷P11963】【动态规划】[GESP202503 六级] 环线

P11963 [GESP202503 六级] 环线

通往洛谷的传送门

题目描述

小 A 喜欢坐地铁。地铁环线有 n n n 个车站,依次以 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n 标号。车站 i   ( 1 ≤ i < n ) i\ (1\leq i<n) i (1i<n) 的下一个车站是车站 i + 1 i+1 i+1。特殊地,车站 n n n 的下一个车站是车站 1 1 1

小 A 会从某个车站出发,乘坐地铁环线到某个车站结束行程,这意味着小 A 至少会经过一个车站。小 A 不会经过一个车站多次。当小 A 乘坐地铁环线经过车站 i i i 时,小 A 会获得 a i a_i ai 点快乐值。请你安排小 A 的行程,选择出发车站与结束车站,使得获得的快乐值总和最大。

输入格式

第一行,一个正整数 n n n,表示车站的数量。

第二行, n n n 个整数 a i a_i ai,分别表示经过每个车站时获得的快乐值。

输出格式

一行,一个整数,表示小 A 能获得的最大快乐值。

输入输出样例

输入 #1

4
-1 2 3 0

输出 #1

5

输入 #2

5
-3 4 -5 1 3

输出 #2

5

说明/提示

对于 20 % 20\% 20% 的测试点,保证 1 ≤ n ≤ 200 1\leq n\leq 200 1n200

对于 40 % 40\% 40% 的测试点,保证 1 ≤ n ≤ 2000 1\leq n\leq 2000 1n2000

对于所有测试点,保证 1 ≤ n ≤ 2 × 10 5 1\leq n\leq 2\times 10^5 1n2×105 − 10 9 ≤ a i ≤ 10 9 -10^9\leq a_i\leq 10^9 109ai109

1.思路解析

    形式化题意
    对于一个环,求他的最大字段和。

    推导
    首先,对于一个环,如下图:
在这里插入图片描述
    我们可以将他从某一条边分开,从而变成一个序列。

    从 1 1 1 2 2 2之间分,序列变成1 0 -4 2 -1 4 3 2
    从 2 2 2 3 3 3之间分,序列变成2 1 0 -4 2 -1 4 3
    …

    我们要在环上选起点和终点,假设我们选择 l = 2 , r = − 1 l=2,r=−1 l=2,r=1的节点,在环上表示:
在这里插入图片描述
    我们把他表示到序列上,如序列为2 1 0 -4 2 1 4的情况。

在这里插入图片描述
    发现,如果要选取一段,只有两种情况:

    (1)选的在序列中间
    (2)选的不在序列中间,观察可知,这种一定是序列的前缀和后缀拼在一起的。

    所以,我们只要对这两种,都求出最大值,最后取个 m a x max max就可以了。

    定义 f 1 i f1_i f1i表示前i个中最大的前缀和 f 2 i f2_i f2i表示后i 个中最大的后缀和预处理 f 1 , f 2 f1,f2 f1,f2,即可避免重复。最后,在对这个序列,跑一遍最大子段和,取个 m a x max max就可以了,还需要记得开long long

2.AC代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int f1[200005],f2[2000005],a[200005];
signed main(){
    int n;
    cin>>n;
    for (int i=1;i<=n;i++)cin>>a[i];
    int sum=0,maxn=-1e9;
    for (int i=1;i<=n;i++) {
        sum+=a[i];
        maxn=max(maxn,sum);
        f1[i]=maxn;
    }
    sum=0,maxn=-1e9;
    for (int i=n;i>=1;i--) {
        sum+=a[i];
        maxn=max(maxn,sum);
        f2[i]=maxn;
    }
    int cnt=-1e9; // 第一种情况的最大值
    sum=0;
    for (int i=1;i<=n;i++){
        if (sum<0)sum=a[i];
        else sum+=a[i];
        cnt=max(cnt,sum);
    }
    int cnt2=-1e9; // 第二种情况的最大值
    for (int i=1;i<=n;++i)
        cnt2=max(max(cnt2,f1[i]+f2[i+1]),f1[i-1]+f2[i]);
    cout<<max(cnt,cnt2);
    return 0;
}

最后,制作不易,希望大家多多点赞收藏,关注下微信公众号,谢谢大家的关注,您的支持就是我更新的最大动力!
公众号上会及时提供信息学奥赛的相关资讯、各地科技特长生升学动态、还会提供相关比赛的备赛资料、信息学学习攻略等。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝胖子教编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值