题目描述
小朋友排队
n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3)
依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少?
如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。
数据格式
输入的第一行包含一个整数n,表示小朋友的个数。
第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。
输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。
例如,输入:
3
3 2 1
程序应该输出:
9
数据规模与约定
对于10%的数据, 1<=n<=10;
对于30%的数据, 1<=n<=1000;
对于50%的数据, 1<=n<=10000;
对于100%的数据,1<=n<=100000,0<=Hi<=1000000。
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 1000ms
思路
求逆序对,每个小朋友被包含的逆序对的个数就是他需要交换的次数t。最后对于所有t求出1…t的和再相加。
求逆序对可以用归并排序
和树状数组
,我写的是归并排序的方法。
AC 代码
#include <iostream>
using namespace std;
struct Child{
int h; //身高
int t = 0; //交换次数
}children[110000];
Child temp[110000];
/*合并两个降序序列*/
void merge(Child* heights, int begin, int middle, int end){
//先拷贝一份原数组
for(int ii = begin; ii<=end;++ii)
temp[ii] = heights[ii];
int i = begin; //前一半数组下标
int j = middle+1; //后一半数组下标
int k = begin; //归并后的大数组下标
while(i <= middle && j <= end){
if(temp[i].h > temp[j].h){
//temp[j]后面的小朋友的h都小于temp[j].h,因此肯定也都小于temp[i].h
temp[i].t += end -j +1;
heights[k++] = temp[i++];
}
else{
//temp[i]前面的小朋友的h都大于temp[j].h
temp[j].t += i - begin;
heights[k++] = temp[j++];
}
}
while(i<=middle)
heights[k++] = temp[i++];
while(j<=end){
//如果第二部分数组有剩余,说明第一部分的小朋友h都大于这些剩余的小朋友
temp[j].t += middle - begin +1;
heights[k++] = temp[j++];
}
}
/* 归并排序 */
void mSort(Child* heights, int begin, int end){
if(begin == end) return;
int middle = (begin + end)/2;
mSort(heights, begin, middle);
mSort(heights, middle+1, end);
merge(heights, begin, middle, end);
}
int main()
{
int n;
cin>>n;
for(int i = 0;i<n;++i)
scanf("%d",&children[i].h);
mSort(children,0,n-1);
//求总的不高兴指数
long long int total = 0;
for(int i = 0;i<n;++i){
long long t = children[i].t;
total += t*(t+1)/2;
}
cout<<total<<endl;
return 0;
}