题目描述:
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
【数据格式】
输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
例如,输入:
3
3 2 1
程序应该输出:
9
【样例说明】
首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。
【数据规模与约定】
对于10%的数据, 1<=n<=10;
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
思路
这里大佬的思路可以借鉴大佬思路
大佬是用c语言实现的,我们这里使用python实现。
根据题意,通过调整最终要得到一个从小到大的数组,其中一个小朋友被调整的次数为n,他的不高兴的程度为,所以我们只要知道每一位小朋友被调换的次数再全部加起来,就得到了我们最终的值
我们该怎么计算每个小朋友被调换的次数呢?这里我们用到了一种数据结构,树状数组。通过使用树状数组,可以很快的算出前缀和。通过前缀和,我们可以算出每个小朋友左边有多少人比他高,右边有多少人比他矮,两者相加,就是这个小朋友的n了。
我们这里用树状数组做两次逆序对,一次是正序处理,从左往右,找到右边矮的,一次是倒序处理,从右往左,找出左边高的。
关于逆序对的介绍可以参考这篇博客树状数组求逆序对
正序处理:
当前已经处理的数字个数减掉当前数字的前缀和即为以该数为较小数的逆序对个数。如图,比如当前已经读了n个数,而前缀和只有n-2个数,说明当前读到的数比之前读到的两个数要小,则找到了两个逆序对
k[i]=i-1-sum(H[i])
逆序处理:
用树状数组倒序处理数列,当前数字的前一个数的前缀和即为以该数为较大数的逆序对的个数。因为是从后往前读,当读到 i 时,发现它的前一个数的前缀和>0,则说明之前读过的数中有小于当前数的,我们将这个值加上前缀和即可
k[i]+=sum(H[i]-1)
代码
1. N=100000 2. 3. def discretization(h): 4. temp=list(set(h)) 5. temp.sort() 6. for i in range(len(h)): 7. h[i]=temp.index(h[i])+1 8. 9. def lowbit(x): 10. return x&-x 11. 12. def update(x,d): 13. while x<N: 14. tree[x]+=d 15. x+=lowbit(x) 16. 17. def sum(x): 18. s=0 19. while x>0: 20. s+=tree[x] 21. x-=lowbit(x) 22. return s 23. 24. n=int(input()) 25. Hold=list(map(int,input().split())) 26. H=[0 for _ in range(N)] 27. for i in range(0,n): 28. H[i+1]=Hold[i]+1 29. 30. k=[0 for _ in range(N)] 31. 32. #discretization(H) 33. tree=[0 for _ in range(N)] 34. for i in range(1,n+1): 35. k[i]=i-1-sum(H[i]) 36. update(H[i],1) 37. 38. tree=[0 for _ in range(N)] 39. for i in range(n,0,-1): 40. k[i]+=sum(H[i]-1) 41. update(H[i],1) 42. res=0 43. for i in range(1,n+1,1): 44. res+=int((1+k[i])*k[i]/2) 45. print(res)
这个代码在蓝桥杯官网会超时,但在报名课程中可以通过,应该是系统问题
感谢罗老师的教程。