问题描述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。
解题思路:
1、我先用了冒泡一下,果断超时。
2、正解:求解逆序对。
方法:树状数组求解逆序对。因为树状数组在求解逆序上有着非常方便的功效。
统计每个小朋友前后的逆序数。然后就可以计算出该小朋友最后会有多少不满意度,最后将所有小朋友的不满意度加一起就是答案。
这里需要注意的是1、可能有小朋友的身高一样。2、身高可能为0!!!只需要输入的时候加一就行了。
唯一麻烦处理的就是身高一样的情况。
我先采用的是输入的时候统计有哪些身高有多少相同的小朋友。先算前逆序数,然后可以用公式算出来后逆序数有多少个。
后面用了网上的一种好的方法,统计两次前逆序数,只不过第二次反着算!
(ps:用long long)
方法一:
#include<iostream> #include<bits/stdc++.h> #define ll long long using namespace std; ll lowbit(ll x){ return x&(-x); } struct Node{ ll heigh,qian,times; } a[100600]; ll flag[1000050]; void add(ll x,ll getsum[]){ while(x<1000050){ getsum[x]++; x+=lowbit(x); } } ll left_query(Node a,ll num,ll getsum[]){ ll x=a.heigh; ll ans=0; while(x!=0){ ans+=getsum[x]; //统计当前序列比x小的个数 x-=lowbit(x); } return num-ans; } ll right_query(Node a,ll num,ll sum,ll getsum[]){ ll x=a.heigh; ll ans=0; while(x!=0){ ans+=getsum[x]; x-=lowbit(x); } ll m=(sum-num)-(sum-ans-a.times)-(flag[a.heigh]-a.qian); return m; } ll getsum[1000050]; int main(){ ll n; cin>>n; for(ll i=1;i<=n;i++){ scanf("%d",&a[i].heigh); a[i].heigh++; flag[a[i].heigh]++; //标记身高 a[i].qian=flag[a[i].heigh]; //统计前面有多少个与他相同身高的数量 a[i].times=0; //移动次数初始化 add(a[i].heigh,getsum); //插入序列 a[i].times=left_query(a[i],i,getsum); //左查询 } ll ans=0; for(ll i=1;i<=n;i++){ a[i].times+=right_query(a[i],i,n,getsum); //右查询 ans+=((1+a[i].times)*a[i].times)/2; } cout<<ans<<endl; return 0; }
方法二:
1 #include<iostream> 2 #include<bits/stdc++.h> 3 #define ll long long 4 using namespace std; 5 ll lowbit(ll x){ 6 return x&-x; 7 } 8 void add(ll x,ll getsum[]){ 9 while(x<1000000){ 10 getsum[x]++; 11 x+=lowbit(x); 12 } 13 } 14 ll left_query(ll x,ll i,ll getsum[]){ 15 ll ans=0; 16 while(x!=0){ 17 ans+=getsum[x]; 18 x-=lowbit(x); 19 } 20 return i-ans; 21 } 22 ll right_query(ll x,ll i,ll getsum[]){ 23 ll ans=0; 24 x--; 25 while(x!=0){ 26 ans+=getsum[x]; 27 x-=lowbit(x); 28 } 29 return ans; 30 } 31 ll a[100500]; 32 ll b[100500]; 33 ll times[100500]; 34 ll getsum[1050000]; 35 int main(){ 36 ll n; 37 cin>>n; 38 ll k=n; 39 for(ll i=1;i<=n;i++){ 40 scanf("%d",a+i); 41 b[k--]=++a[i]; 42 add(a[i]+1,getsum); 43 times[i]=left_query(a[i]+1,i,getsum); 44 } 45 k=n; 46 memset(getsum,0,sizeof(getsum)); 47 for(ll i=1;i<=n;i++){ 48 add(b[i]+1,getsum); 49 times[k--]+=right_query(b[i]+1,i,getsum); 50 } 51 ll sum=0; 52 for(ll i=1;i<=n;i++){ 53 sum+=((1+times[i])*times[i])/2; 54 } 55 cout<<sum<<endl; 56 return 0; 57 }