AcWing 1248. 灵能传输

10 篇文章 0 订阅

这是我做过最难最有意思的题吧。
在这里插入图片描述
这里面的规律很有趣,要是没学过前缀和确实不会很容易发现里面的规律。
我们把每一个数用前缀和来表示,如果可以选择“传递灵能”的话,我们发现
s[i-1]=s[i-1]+a[i]=s[i]
s[i]-2a[i]+a[i]=s[i-1]
s[i+1]-2a[i]+a[i]+a[i]=s[i+1]
由此可见与其说传递灵能,我们可以把他表示为一个s[i]于s[i-1]的互换过程,
然后我们想要求相邻的s[i]与s[i+1]的差最小,我们正常的思路是,单调递增,他们之间的差一定是最小的,后来看老师的视频了解到,还有一个可以更小的办法,两个拐点??好像这部分斜率也是相当的低,所以可能会有最小值吧。(其实不是这样,这是某一位同学说的)
我后来注意到Sn这个点是没法转换的,也就是说,无法组成一个完美的单调递增序列,那么怎么找一个区间比较小的呢,就有了视频讲解中的一幕(跳点)
在这里插入图片描述
按照这样的思路的话,那我们怎么去实现这个呢,老师神奇的给我们来了一种跳点(有点像打麻将了)
在这里插入图片描述
这样跳,刚开始我好像也是没看懂,后来在代码的实现上,才看懂了跳点的一个操作,不得不说确实有点巧妙。
我的代码和老师代码还是很相似的,老师代码有点快接近最优解了哈哈,老师写的代码我找不到优化的地步,但是我的代码还是没有老师好。
老师代码

https://www.acwing.com/solution/content/8433/

我的代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=3e5+10;
LL s[N],z[N],res=0;
int T,n;
bool st[N];
int main(void)
{
    cin>>T;
    while(T--)
    {
        cin>>n;res=0;
        int l=0,r=n;
        memset(s,0,sizeof s);
        memset(z,0,sizeof z);
        for(int i=1;i<=n;i++)
        cin>>s[i],s[i]+=s[i-1];
        LL s0=s[0],sn=s[n];
        if(s0>sn)
        swap(s0,sn);
        sort(s,s+n+1);
        for(int i=0;i<=n;i++)
        if(s[i]==s0)
        {s0=i;break;}
        for(int i=n;i>=0;i--)
        if(s[i]==sn)
        {sn=i;break;}
        memset(st,0,sizeof st);
        for(int i=s0;i>=0;i-=2)
        z[l++]=s[i],st[i]=true;
        for(int i=sn;i<=n;i+=2)
        z[r--]=s[i],st[i]=true;
        for(int i=0;i<=n;i++)
        if(!st[i])
        z[l++]=s[i];
        for(int i=1;i<=n;i++)
        res=max(res,abs(z[i]-z[i-1]));
        cout<<res<<endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值