蓝桥杯·小朋友排队问题 Python题解整理(归并排序+树状数组)

原题:

思路:

其实就是关于逆序对的问题。每个小朋友的不高兴程度是以其交换次数为等差数列的和(an=n),而每个小朋友的交换次数则取决于(排在他前面还比他高的人)+(排在他后面还比他矮的人)。

方法:

最简单的用暴力遍历,但TLE时间受限。

这里可以考虑归并排序和树状数组,时间复杂度都是 O(nlogn)

题解:

1.归并排序+二分

思路参考:数据结构--归并排序的应用(求逆序数 蓝桥杯--小朋友排队) - ruo_yu - 博客园

def merge(a,l,r):   
  if l>=r:  return
  mid=(l+r)//2
  tmp,i,j=[],l,mid+1
  merge(a,l,mid)
  merge(a,mid+1,r)

  while i<=mid and j<=r:
    if a[i]<=a[j]:
      tmp.append(a[i])
      i+=1
    else:
      tmp.append(a[j])
      high[a[j]]+=mid-i+1   #从i到mid的每个数与j组成逆序对,求出小朋友的high值
      j+=1
  if i<=mid:
    tmp.extend(a[i:mid+1])
  elif j<=r:
    tmp.extend(a[j:r+1])
  a[l:r+1]=tmp[:]

def binary(a,k):    #二分查找数组中与k相等的左边界下标
    l,r=0,len(a)
    while l<r:
        mid=(l+r)//2
        if a[mid]<k:
            l=mid+1
        elif a[mid]>=k:
            r=mid
    return l
        
n=int(input())
h=[-1]+list(map(int,input().split()))
pos,low,high=[0]*(n+1),[0]*(n+1),[0]*(n+1)
#pos[k]:第k高小朋友在队中的位置
#low[k]:第k高小朋友后面比它矮的小朋友数量
#high[k]:第k高小朋友前面比它高的小朋友数量

a=h[:]
a.sort()
for i in range(1,n+1):          #对h[]重新编号
    k=binary(a,h[i])
    h[i]=k                      #身高从低到高重新编号
    pos[h[i]]=i                 #第k矮小朋友排在原队列的第i个
    a[k]-=1                     #避免重复查找,再次查到a[k]的时候查到的是后一个

merge(h,1,n)
ans=0
for i in range(1,n+1):
    k=pos[i]                    #第i矮的小朋友编号
    w=i                         #w表示第i矮的
    low[i]=w-1-(k-1-high[i])    #w-1表示比k低的总人数,k-1-high表示排在k前面比k低的人数
    t=high[i]+low[i]
    ans+=t*(t+1)//2
print(ans)

2.树状数组 

注意树状数组中记录的是每个数出现的次数

n=int(input())
N=1000010
h=[-1]+[int(i)+1 for i in input().split()]  
c=[0]*N            #树状数组
cnt=[0]*N          #记录每个小朋友的交换次数
m=max(h)

def lowbit(n):
    return n&(-n)

def update(x):
    i=x
    while i<=m:
        c[i]+=1
        i+=lowbit(i)

def getsum(x):
    sum,i=0,x
    while i>=1:
        sum+=c[i]
        i-=lowbit(i)
    return sum

#求左边比他大的数
for i in range(1,n+1):
    update(h[i])
    cnt[i]+=i-getsum(h[i])    #i是总人数,getsum(h[i])是小于等于h[i]的人数

#求右边比他小的数
c=[0]*N
for i in range(n,0,-1):
    update(h[i])
    cnt[i]+=getsum(h[i]-1)

ans=0
for i in range(1,n+1):
    ans+=cnt[i]*(cnt[i]+1)//2
print(ans)

使用树状数组时格外注意下标从1开始,而本题小朋友的身高有可能是0,所以把身高整体+1,既不会影响逆序对的个数,也防止lowbit(0)=0而出现死循环的情况。

提交 

由于当时未设立Python组,所以1s的时间可能有些超时,大家可以在C语言网评测一下。

C语言网 - 编程入门学习 - 实用的编程在线学习网站 (dotcpp.com)

菜鸟拙见,如有错误请大家不吝赐教! 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eureka!!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值