【通俗易懂能AC】2022年第十三届蓝桥杯python B组省赛题解分享

2022年第十三届蓝桥杯python B组省赛题解分享

前言

是python组报名的基数太少了吗!!csdn上居然找不到ac的题解!只能自己钻研自己写了qwq

消除游戏

思路

这道题暴力能过75%的样例。

AC的数据结构是用双链表存字符串,因为用双链表删除一个字符的时间复杂度是 O ( 1 ) O(1) O(1)。其实是在暴力思路上做优化,暴力思路就是我每次都遍历字符串,找到当前所有的边缘字符,不断删除,直到字符串为空或者删除前后字符串没有发生变化为止。优化思路是,首先遍历一次字符串,找到边缘字符,然后只需要不断判断边缘字符左右2侧的字符是否为边缘字符,如果已经被判断为边缘字符过,就不要再重复判断了。这样做,第一次遍历时间复杂度是 O ( n ) O(n) O(n),后面找新的边缘字符时,每个字符最多被找一遍,也是 O ( n ) O(n) O(n) 的,所以整体时间复杂度是 O ( n ) O(n) O(n)

代码

蓝桥云课和Acwing上都能ac,但是new online judge上只能过80%的样例。

N = 1000010
l, r, st = [-1]*N, [-1]*N, [False]*N
edge = []

def remove(x):
    r[l[x]] = r[x]; l[r[x]] = l[x]

def check(x): # 判断x是否为边缘字符
    if s[l[x]]=='@' or s[r[x]]=='@': return
    if s[x]==s[r[x]] and s[x]!=s[l[x]]:
        edge.append(x); edge.append(l[x])
    if s[x]==s[l[x]] and s[x]!=s[r[x]]:
        edge.append(x); edge.append(r[x])

if __name__=='__main__':
    s = input(); n = len(s); s = '@'+s+'@'
    for i in range(1,n+1):
        l[i] = i-1; r[i] = i+1
    
    for i in range(1, n+1): check(i) # 初次检查边缘字符
    
    i = 0
    while i < len(edge): # 删除边缘字符 && 找新出现的边缘字符
        pot = set()
        while i<len(edge):
            j = edge[i]
            if st[j]==True: i+=1; continue
            remove(j); st[j] = True
            pot.add(l[j]); pot.add(r[j])
            i+=1

        for k in pot:
            if st[k]==False: check(k)

    p = False
    for i in range(1, n+1):
        if st[i]==False: print(s[i], end=''); p=True
    if p==False: print('EMPTY')
    # res = ''
    # for i in range(1, n+1):
    #     if st[i]==False: res+=s[i]
    # print('EMPTY' if res=='' else res)
    

技能升级

思路

解决此题有3重境界。

第一层境界,从题干里剥出核心问题。N个技能,每个技能可以提升的攻击力是成等差数列递减的,问最多可以提升多少攻击力,那么一定是从所有数里面选最大的m个。

第二层境界,用最大堆去维护所有数,每次从堆里删除一个数的时间复杂度是 O ( l o g n ) O(logn) O(logn),选出m个数的时间复杂度是 O ( m l o g n ) O(mlogn) O(mlogn)。python中可以用heapeq模块来模拟最小堆,把每个数取相反数存进堆中,就可以实现最大堆。

第三层境界,比较奇葩。我们最终的目的是在所有数中挑出前m个最大的数,那么可以用二分找出第m个大的数 x x x 。二分的范围是 [ 0 , 1 0 6 ] [0, 10^6] [0,106],并且此序列具有二段性,一边的区间 > = x >=x >=x 的数 < m <m <m 个,另一边的区间 > = x >=x >=x 的数 > = m >=m >=m 个,二分的时间复杂度是 O ( l o g n ) O(logn) O(logn)。接下来找出每个技能中, > = x >=x >=x 的数的个数。由于每个技能的数都是单调递减的等差数列,可以用等差数列的公式求得个数 ⌊ ( a i − x ) b i ⌋ + 1 \lfloor\frac{(a_i-x)}{b_i}\rfloor+1 bi(aix)+1。计算每个技能提升的攻击力总和,也只需要用等差数列求和公式 ( a 1 + a n ) n 2 \frac{(a_1+a_n)n}{2} 2(a1+an)n 即可。

代码

一:绝对暴力
acwing上通过9/11样例,new online judge上过10%,蓝桥云课过40%。

N = 100010
if __name__=='__main__':
    a, b = [], []
    n, m = map(int, input().split())
    for _ in range(n):
        aa, bb = map(int, input().split())
        a.append(aa); b.append(bb)
    nums = []
    for i in range(n):
        aa, bb = a[i], b[i]
        while aa>0:
            nums.append(aa)
            aa -= bb
    nums.sort(reverse = True) # 降序
    res = 0
    for i in range(min(m, len(nums))):
        res += nums[i]
    print(res)

二:堆优化
acwing上通过7/12样例。new online judge居然内存超限,过10%。,蓝桥云课过40%。

import heapq
N = 100010
if __name__=='__main__':
    a, b = [], []
    n, m = map(int, input().split())
    for _ in range(n):
        aa, bb = map(int, input().split())
        a.append(aa); b.append(bb)
    nums = []
    for i in range(n):
        aa, bb = a[i], b[i]
        while aa>0:
            heapq.heappush(nums, -aa)
            aa -= bb
    res = 0
    for i in range(min(m, len(nums))):
        res += -heapq.heappop(nums)
    print(res)

三:二分
AC!!

N = 100010
def check(mid):
    global m
    cnt = 0
    for i in range(n):
        if a[i]>=mid: cnt += (a[i]-mid)//b[i]+1
    return cnt>=m
    
if __name__=='__main__':
    global m
    a, b = [], []
    n, m = map(int, input().split())
    for _ in range(n):
        aa, bb = map(int, input().split())
        a.append(aa); b.append(bb)
    # 二分找分界点
    l, r =0, 1000010
    while l<r:
        mid = (l+r+1)//2
        if check(mid): l=mid
        else: r=mid-1
    # 计算>=x的数的总和/个数
    count, sum = 0, 0
    for i in range(n):
        if a[i]>=r:
            c = (a[i]-r)//b[i]+1
            end = a[i]-(c-1)*b[i]
            count += c
            sum += (a[i]+end)*c//2
    print(sum - (count-m)*r)

后记

期末考试跟国赛撞了,后面没怎么写了。现在国赛也考完了。以后有时间再填坑。如果对大家有帮助的话,欢迎点赞收藏~!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值