基础算法题——小朋友排队(树状数组)

小朋友排队

问题描述
  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。


题目分析

暴力枚举:判断每一个元素右边有多少个元素小于本身,再判断每一个元素左边有多少个元素大于本身,得到每个元素的交换次数。用这种解法,只能得到30%的分数。
这里就可以引出今天我们的重头戏——树状数组

应用场景:快速求区间和、前缀和
①、前缀和数组(静态数组)
新开一个数组b:每一个元素为对象数组a初始到目前的和
eg:b[0]=a[0] b[1]=a[0]+a[1] … b[10]=a[0]+…+a[10]
这里有个弊端:如果a数组中a[10]变化了,那b[10]到末尾的数据都要改。
时间复杂度会大大增加。

②、性能优化:树状数组(动态数组)树状数组参考资料
树状数组
每更新a数组维护一个C数组。
更新 updata:(向后,影响C数组后面的部分元素)
C[k]的含义是数组a上某个区间(k-lowbit(k),k]和
ps:lowbit(k)是k二进制最右边的1代表的整数
lowbit(整数k)=k转换为二进制后最右边的1代表的整数
lowbit(6)=2 ps:6的二进制为110
C6=sum(4,6]=a[5]+a[6]
维护C数组:利用二进制每次移动就倍增的特点
假设C[index]被改动,影响了C[index+lowbit(index)]
C[index+lowbit(index)]被改动,影响C[index+lowbit(index)+lowbit(index+lowbit(index))]

当index为奇数时,本身C[index]不会被影响
当index为2n时,C[2k]都会被影响(k>n)

获取 getSum:(向前,得到C数组前面部分元素和)
计算前11项和sum11=C[10]+C[8]=C[1010]+C[1000]
计算前13项和sum13=C[12]+C[8]=C[1100]+C[1000]
计算前15项和sum15=C[14]+C[12]+C[8]=C[1110]+C[1100]+C[1000]
计算第12项到第15项的和(不包含第12项)
sum=sum15-sum11
=C[14]+C[12]+C[8]-(C[10]+C[8])
=C[1110]+C[1100]+C[1000]-(C[1010]+C[1000])

树状数组的作用:快速求出区间和
树状数组本身没有意义,只有在a数组中才用实际意义

int lowbit(int n)
{
   
	return n&(-n);//取n二进制下最右边的1 
} 

void updata(int n, int i, int v, int *c)
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值