蓝桥杯-小朋友排队

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

 

转载于:https://www.cnblogs.com/ISGuXing/p/8540559.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值