题目链接:http://poj.org/problem?id=2299
题意就是求把数组按从小到大的顺序排列,每次只能交换相邻的两个数, 求至少交换了几次
就是求逆序数
#include<stdio.h> #include<string.h> #include<algorithm> #define N 501000 using namespace std; int a[N], b[N]; __int64 cnt; void Merge(int r, int mid, int l) { int i = r, j = mid+1, k = 0; while(i<=mid && j<=l) { if(a[i] > a[j]) { b[k++] = a[j++]; cnt += mid-i+1; ///说明下标从i到mid的值都比a[j]大所以逆序数要+mid-i+1; } else if(a[i] < a[j]) { b[k++] = a[i++]; } else { b[k++] = a[i++]; j++; } } while(i<=mid) b[k++] = a[i++]; while(j<=l) b[k++] = a[j++]; for(i=0; i<k; i++)///将有序的临时数组 元素 刷回 被排序的数组 a 中, a[i+r] = b[i]; } void MergeSort(int r, int l) { int mid = (r+l)/2; if(r < l) { MergeSort(r, mid);///对前半部分进行排序 MergeSort(mid + 1, l);///对后半部分进行排序 Merge(r, mid, l);/// 合并前后两部分 } } int main() { int n; while(scanf("%d", &n),n) { cnt = 0; for(int i=0; i<n; i++) scanf("%d", &a[i]); MergeSort(0, n-1); printf("%I64d ", cnt); } return 0; }
用树状数组
序列3 5 4 8 2 6 9
大体思路为:新建一个数组,将数组中每个元素置0
0 0 0 0 0 0 0
取数列中最大的元素,将该元素所在位置置1 统计该位置前放置元素的个数,为0
0 0 0 0 0 0 1
接着放第二大元素8,将第四个位置置1 统计该位置前放置元素的个数,为0
0 0 0 1 0 0 1
继续放第三大元素6,将第六个位置置1 统计该位置前放置元素的个数,为1
0 0 0 1 0 1 1
…
这样直到把最小元素放完,累加每次放元素是该元素前边已放元素的个数,这样就算出总的逆序数来了在统计和计算每次放某个元素时,该元素前边已放元素的个数时如果一个一个地数,那么一趟复杂度为O(n),总共操
作n趟,复杂度为O(n^2),然而复杂度太大,
当然,在每次统计的过程中用树状数组可以把每一趟计数个数的复杂度降为O(logn),这样整个复杂度就变为O(nlogn)
树状数组是一种很好的数据结构,这有一篇专门描述树状数组的文章
将序列中的每个数按照从大到小的顺序插入到树状数组中,给当前插入节点及其到根节点之间的这条路径上的每个数加1,然后统计该节点右边放置元素的个数
#include<stdio.h> #include<string.h> #include<iostream> #include<algorithm> using namespace std; #define N 501000 #define met(a, b) memset(a, b, sizeof(a)) int Hash[N], n; __int64 Tree[N]; struct node { int V, id; ///重载运算符中:<是从大到小,>是从小到大; friend bool operator < (node a, node b) { return a.V < b.V; } }a[N]; int lowbit(int x) { return x&(-x); } void Update(int pos, int num) { while(pos<=n) { Tree[pos] += num; pos += lowbit(pos); } } __int64 GetSum(int pos) { __int64 s=0; while(pos) { s += Tree[pos]; pos -= lowbit(pos); } return s; } int main() { while(scanf("%d", &n), n) { met(a,0); met(Tree, 0); met(Hash, 0); for(int i=1; i<=n; i++) { scanf("%d", &a[i].V); a[i].id = i; } sort(a+1, a+n+1);///按V从大到小排序; for(int i=1; i<=n; i++) Hash[a[i].id] = i;///离散化后的Hash数组大小关系顺序和输入的数组大小关系一样; __int64 ans=0; for(int i=1; i<=n; i++) { int pos = Hash[i]; Update(pos, 1);///每插入一个数时,就把那个位置置为1; int m = GetSum(pos);///从第一个元素到第pos个元素的和,即m=前pos个元素中小于pos的个数; ans += i-m;///所以逆序数应该加上比pos个元素大的,即总个数-比pos小的; } printf("%I64d ", ans); } return 0; }
也可以用线段树来写,每次更新pos点,然后在求1-pos的和即可
#include <iostream> #include <stdio.h> #include <math.h> #include <string.h> #include <queue> #include <stack> #include <algorithm> #include <map> #include <string> typedef long long LL; #define INF 0x3f3f3f3f #define met(a, b) memset(a, b, sizeof(a)) #define N 500005 using namespace std; #define Lson r<<1 #define Rson r<<1|1 struct node { int L, R; LL sum; int Mid(){return (L+R)/2;} }a[N*4]; struct Num { int num, pos; friend bool operator < (Num p, Num q) { return p.num > q.num; } }b[N]; void Build(int r, int L, int R) { a[r].L = L; a[r].R = R; if(L == R) return; Build(Lson, L, a[r].Mid()); Build(Rson, a[r].Mid()+1, R); } void Update(int r, int pos, int num) { if(a[r].L == pos && a[r].R == pos) { a[r].sum += num; return ; } if(pos > a[r].Mid()) Update(Rson, pos, num); else if(pos <= a[r].Mid()) Update(Lson, pos, num); a[r].sum = a[Lson].sum + a[Rson].sum; } LL Query(int r, int L, int R) { if(L > R) return 0; if(a[r].L == L && a[r].R == R) return a[r].sum; if(R <= a[r].Mid()) return Query(Lson, L, R); else if(L > a[r].Mid()) return Query(Rson, L, R); else { LL ans1 = Query(Lson, L, a[r].Mid()); LL ans2 = Query(Rson, a[r].Mid()+1, R); return ans1 + ans2; } } int main() { int n; while(scanf("%d", &n), n) { met(a, 0); met(b, 0); for(int i=1; i<=n; i++) { scanf("%d", &b[i].num); b[i].pos = i; } Build(1, 1, n); sort(b+1, b+n+1); LL ans = 0; for(int i=1; i<=n; i++) { Update(1, b[i].pos, 1); ans += Query(1, 1, b[i].pos-1); } printf("%I64d ", ans); } return 0; }