zoukankan      html  css  js  c++  java
  • 洛谷 P1908 逆序对 Label:归并排序||树状数组 不懂

    题目描述

    猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

    输入输出格式

    输入格式:

    第一行,一个数n,表示序列中有n个数。

    第二行n个数,表示给定的序列。

    输出格式:

    给定序列中逆序对的数目。

    输入输出样例

    输入样例#1:
    6
    5 4 2 6 3 1
    
    输出样例#1:
    11

    说明

    对于50%的数据,n≤2500

    对于100%的数据,n≤40000。

    代码:解法一

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 int N,a[100005],temp[100005],cnt;
     7 
     8 void print(){
     9     for(int i=1;i<=6;i++){
    10         printf("%d ",a[i]);
    11     }
    12     puts("");
    13 }
    14 
    15 void merge_sort(int l,int r){
    16     if(l>=r) return;
    17     int mid=(l+r)>>1;
    18     merge_sort(l,mid);merge_sort(mid+1,r);
    19     
    20     
    21     int i=l,j=mid+1,point=l;
    22     while(i<=mid&&j<=r){
    23         if(a[i]>a[j]){
    24             temp[point++]=a[j++];
    25             cnt+=(mid-i+1);
    26         }
    27         else{
    28             temp[point++]=a[i++];
    29         }
    30     }
    31     
    32     while(i<=mid) temp[point++]=a[i++];
    33     while(j<=r)   temp[point++]=a[j++];
    34     
    35     for(int k=l;k<=r;++k)
    36         a[k]=temp[k];
    37 //    print();
    38 }
    39 
    40 int main(){
    41 //    freopen("01.txt","r",stdin);
    42     
    43     scanf("%d",&N);
    44     for(int i=1;i<=N;++i)
    45         scanf("%d",&a[i]);
    46     
    47     merge_sort(1,N);
    48     
    49     printf("%d
    ",cnt);
    50     return 0;
    51 }

    暴力枚举的话,可以过两个点~

    转载题解如下:http://blog.csdn.net/acdreamers/article/details/16849761

    归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。

    在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在

    前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并

    排序中的合并过程中计算逆序数.

    解法二:树状数组+离散化

     1 #include <iostream>  
     2 #include <cstring>  
     3 #include <cstdio>  
     4 #include <algorithm>  
     5 using namespace std;  
     6   
     7 const int N = 500005;  
     8   
     9 struct Node  
    10 {  
    11     int val;  
    12     int pos;  
    13 };  
    14   
    15 Node node[N];  
    16 int c[N], reflect[N], n;  
    17   
    18 bool cmp(const Node& a, const Node& b)  
    19 {  
    20     return a.val < b.val;  
    21 }  
    22   
    23 int lowbit(int x)  
    24 {  
    25     return x & (-x);  
    26 }  
    27   
    28 void update(int x)  
    29 {  
    30     while (x <= n)  
    31     {  
    32         c[x] += 1;  
    33         x += lowbit(x);  
    34     }  
    35 }  
    36   
    37 int getsum(int x)  
    38 {  
    39     int sum = 0;  
    40     while (x > 0)  
    41     {  
    42         sum += c[x];  
    43         x -= lowbit(x);  
    44     }  
    45     return sum;  
    46 }  
    47   
    48 int main()  
    49 {  
    50     while (scanf("%d", &n) != EOF && n)  
    51     {  
    52         for (int i = 1; i <= n; ++i)   
    53         {  
    54             scanf("%d", &node[i].val);  
    55             node[i].pos = i;  
    56         }  
    57         sort(node + 1, node + n + 1, cmp);   //排序  
    58         for (int i = 1; i <= n; ++i) reflect[node[i].pos] = i;   //离散化  
    59         for (int i = 1; i <= n; ++i) c[i] = 0;   //初始化树状数组  
    60         long long ans = 0;  
    61         for (int i = 1; i <= n; ++i)  
    62         {  
    63             update(reflect[i]);  
    64             ans += i - getsum(reflect[i]);  
    65         }  
    66         printf("%lld
    ", ans);  
    67     }   
    68     return 0;  
    69 }  

    转载自http://blog.csdn.net/alongela/article/details/8142965

    给定n个数,要求这些数构成的逆序对的个数。除了用归并排序来求逆序对个数,还可以使用树状数组来求解。

    树状数组求解的思路:开一个能大小为这些数的最大值的树状数组,并全部置0。从头到尾读入这些数,每读入一个数就更新树状数组,查看它前面比它小的已出现过的有多少个数sum,然后用当前位置减去该sum,就可以得到当前数导致的逆序对数了。把所有的加起来就是总的逆序对数。

    题目中的数都是独一无二的,这些数最大值不超过999999999,但n最大只是500000。如果采用上面的思想,必然会导致空间的巨大浪费,而且由于内存的限制,我们也不可能开辟这么大的数组。因此可以采用一种称为“离散化”的方式,把原始的数映射为1-n一共n个数,这样就只需要500000个int类型的空间。

    离散化的方式:

    struct Node

    {

    int val;

    int pos;

    };

    Node node[500005];

    int reflect[500005];

    val存放原数组的元素,pos存放原始位置,即node[i].pos = i。

    把这些结构体按照val的大小排序。

    reflect数组存放离散化后的值,即reflect[node[i].pos] = i。

    这样从头到尾读入reflect数组中的元素,即可以保持原来的大小关系,又可以节省大部分空间。

    这个可以辅助理解

     http://blog.csdn.net/cattycat/article/details/5640838

    树状数组实际上还是一个数组,只不过它的每个元素保存了跟原来数组的一些元素相关的结合值。

    若A为原数组,定义数组C为树状数组。C数组中元素C[ i ]表示A[ i –lowbit( i ) + 1]至A[ i ]的结合值。

    lowbit(i)是i的二进制中最后一个不为零的位数的2次方,可以这样计算

    lowbit(i)=x&(-x)

    lowbit(i)=x&(x^(x-1))

    版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!
  • 相关阅读:
    时间戳 时间 相互转换
    CS Academy Remove Update
    一周水题集锦 2017 9.4
    计蒜客 16877 卡牌游戏
    计蒜客 16876 韩梅梅的抽象画
    九度OJ 题目1534:数组中第K小的数字
    CS Academy Switch the Lights
    CF AIM Tech Round 4 C. Sorting by Subsequences
    CF Round 430 C. Ilya And The Tree
    CS Academy Round 44 Check DFS
  • 原文地址:https://www.cnblogs.com/radiumlrb/p/5811757.html
Copyright © 2011-2022 走看看