最大子段和(交换)
Posted wish-all-ac
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大子段和(交换)相关的知识,希望对你有一定的参考价值。
题意:
给你一个序列找到交换一次后的最大字段和(所有交换方式中最大的)
分析:
首先我们这么想,我们要最后要的答案是由某个区间和区间左边交换一个或和区间右边交换一个或者不交换(内部交换或外部交换)。
至于不交换,我们直接o(n)搞过。
然后就是向左(向右类似,只要把序列倒过来跑一边就好了)交换,我们可以得到:ans=max(sum[i]-sum[j]-a[k1]+a[k2])(i>j,j<k1<=i,k2<=j),我们只要n的4次方枚举就好了。
可是我们怎么优化呢?
我们加一个括号:sum[i]-(sum[j]+a[k1]-a[k2])(范围略去),然后我们就可以n的效率枚举i,然后提前预处理一下min(sum[j]+a[k1]-a[k2]),用一个数组记录一下min(枚举合法的j,k1,k2),然后求一下前缀max就好了,效率到了n的3次方,然后类似的,我们只需要n的效率枚举k1,然后n×n求sum[j]-a[k2]的最小值,效率变成了n×n,最后再n的效率枚举j,n的效率求k2的最小值,效率变成了n,最后整合到一起就好了。
代码:
#include <cstdio> #include <string> using namespace std; #define ll long long const int maxn=5e4+10; const ll inf=1e18; ll a[maxn]; ll sum[maxn]; ll b[maxn]; int main(){ int n; scanf("%d",&n); ll mi=0ll,ans=-inf,nowj=0ll,ma=-inf; for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); sum[i]=sum[i-1]+a[i]; ans=max(ans,sum[i]-nowj); nowj=min(nowj,a[i]+mi); ma=max(ma,a[i]); mi=min(sum[i]-ma,mi); } mi=0ll; for(int i=1;i<=n;i++){ ans=max(ans,sum[i]-mi); mi=min(sum[i],mi); } for(int i=1;i<=n;i++) b[i]=a[n-i+1]; for(int i=1;i<=n;i++) a[i]=b[i]; mi=0ll,nowj=0ll,ma=-inf; for(int i=1;i<=n;i++){ sum[i]=sum[i-1]+a[i]; ans=max(ans,sum[i]-nowj); nowj=min(nowj,a[i]+mi); ma=max(ma,a[i]); mi=min(sum[i]-ma,mi); } printf("%lld",ans); return 0; }
以上是关于最大子段和(交换)的主要内容,如果未能解决你的问题,请参考以下文章