蓝桥杯——2014省赛小朋友排队 逆序对求法(归并排序)C/C++

题目描述

小朋友排队

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值