第十届蓝桥杯真题-灵能传输

题目

请添加图片描述
请添加图片描述

OJ

https://www.lanqiao.cn/problems/196/learning/

考点

前缀和、贪心

思路

题目意思就是希望通过灵能交换后使得不稳定度最小,假设对a[i]进行灵能传输,可以发现前缀和s[i-1]和s[i]进行了交换。(不要问我怎么发现的,我一开始也没发现)拿草稿纸很快就能证明。
发现的现象:对i灵能传输,就会交换i-1、i的前缀和
也就是说现在可以任意对第1到第n-1个的前缀和进行交换(为什么不是0-n ?因为只能对2~n-1进行灵能交换)
现在稳定度最小也就变为了相邻前缀和最大差的最小化。
假设没有限制,那么按照数的大小进行排序就可以得到解
证明:

  1. 理论法:如果相邻前缀和差小,那么对于第i个前缀和的邻居i-1、i+1,都是相比其他前缀和最接近的。也就是说没有人可以在中间横插一脚,可以把这个过程看作排序算法中的插入排序,直到无法横插一脚为止。
  2. 画图:
    请添加图片描述
    但是注意到:第一个前缀和s[0]和最后一个前缀和s[n]无法交换,且相对大小关系不确定,所以要在上面的方案进行改进。
    我们可以选择隔几个选一个然后折返,由贪心可知隔一个选一个最优,这时面临先min还是先max的问题
    手动画图证明如下
    请添加图片描述
    当然还有第三种情况,s0 == sn,这个时候其实先min或者先max都一样。
    上面的两种情况最后可以合并为一种,第二种情况sn<s0,直接交换两者即可(反正是求差的绝对值)
    隔一个选一个,可以使用一个等长的数组进行标记是否已经取过了。

特殊情况讨论

  • 存在元素和s0、sn相等选哪个作为起点终点?
    手动证明可以发现,其实选哪个都一样,结果相同
  • s0 == sn时 到底选哪个位置?
    其实位置一样都可以(具体看代码怎么写,但是不建议s0和sn的位置是同一个)
    查找index都是从前往后找:可以判断 s0是否和sn相等,如果相等sn的index直接加一
    最优解决方法:s0的index从前往后找,sn的index从后往前找

代码

写的for有点多,但是更好理解。

t = int(input())
for i in range(t):
    n = int(input())
    a = [0]+list(map(int,input().split()))
    b = [0]*(n+1)
    ## 建立前缀和
    for i in range(1,n+1):
        b[i] = b[i-1]+a[i]
    s0 = min(0,b[n])
    sn = max(0,b[n])
    b.sort()
    s0index = b.index(s0)
    snindex = b.index(sn)
    ## 避免特殊情况 s0 == sn使得min到max这段出错
    if s0index == snindex:
        snindex += 1
    c = []
    ## flag 标记是否取过
    flag = [0]*(n+1)
    ## 标记s0到min
    for i in range(s0index,-1,-2):
        flag[i] = 1
    ## 标记sn到max
    for i in range(snindex,n+1,2):
        flag[i] = 1
    ## 插入s0到min这一段
    for i in range(s0index,-1,-1):
        if flag[i] == 1:
            c.append(b[i])
    ## 插入min到max这一段
    for i in range(n+1):
        if flag[i] == 0:
            c.append(b[i])
    ## 插入max到sn这一段
    for i in range(n,snindex-1,-1):
        if flag[i] == 1:
            c.append(b[i])
    ans = 0
    ## 计算不稳定度
    for i in range(1,n+1):
        if abs(c[i]-c[i-1]) > ans:
            ans = abs(c[i]-c[i-1])
    print(ans)

不愧是压轴题,这25分不好拿。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值