题目
TUOJ
https://sim.csp.thusaac.com/contest/36/problem/1
思路
核心思路:通过预处理前缀和及其前缀/后缀最大值,高效计算当第 i 个消耗 被置为0时,全过程路径中出现的历史最高峰值。
代码
一、问题设定
-
有一条路径,共有
n+1个节点(编号 0 到 n) -
每个节点
i有一个获得能量值a[i] -
从节点
i出发到下一个节点需要消耗能量b[i](b[0]未使用) -
能量在路径中会不断累积和消耗,任何时候能量不能为负数
二、主要计算步骤
1. 计算累计净能量
从起点 0 开始,依次累加每个节点的获得能量并减去消耗能量,得到到达每个位置时的净能量值 sum[i]。这个值反映了走到该位置时的能量盈余或亏损。
2. 计算前缀最大值
记录从起点到每个位置的过程中,出现过的最大净能量值。这个值代表了前半段路径中,需要准备的最低能量底线。
3. 计算后缀最大值
从终点向起点方向,记录从每个位置到终点过程中出现过的最大净能量值。这个值反映了后半段路径的能量需求。
4. 计算每个起点的答案
对于每个可能的起点位置,答案由两部分中的较大者决定:
-
前半段路径需要的能量
-
后半段路径需要的能量(加上当前节点的消耗)
三、算法特点
-
通过一次正向遍历和一次反向遍历完成所有预处理
-
最终对每个起点只需 O(1) 时间就能得到答案
-
整体时间复杂度为 O(n),空间复杂度为 O(n)
cpp
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N=1e5+5;
int a[N], b[N],sum[N],pre_max[N],suf_max[N];
void solve()
{
int n; cin>>n;
for(int i=0;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
sum[0]=a[0]; //走到i,可以继续往下走所需的最小初始能量
pre_max[0]=sum[0];
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i]-b[i];
pre_max[i]=max(pre_max[i-1], sum[i]);
}
suf_max[n]=sum[n];
for(int i=n-1;i>=1;i--)
suf_max[i]=max(suf_max[i+1],sum[i]);
for(int i=1;i<=n;i++)
{
int ans=max(pre_max[i], suf_max[i]+b[i]);
cout<<ans<<" ";
}
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0);
solve();
return 0;
}