poj2299 - Ultra-QuickSort (求逆序数)
题意:给出长度为n的序列,每次只能交换相邻的两个元素,问至少要交换几次才使得该序列为递增序列。
我们需要知道:逆序数 = 在只允许相邻两个元素交换的条件下,得到有序序列的交换次数
所以我们需要求数列的逆序数,O(N*N)的算法肯定会超时的,
所有我们寻求较为高效的排序方法,归并排序就是充分利用分治法的而提高效率的排序方法。
归并排序:
#include <cstdio> #define M 500005 int n , s[M]; long long ans; void move(int l, int mid, int r) { int len = r-l+1; int *a = new int[len+2]; int li = l, ri = mid+1, i = 1; for(; li<=mid&&ri<=r;) if(s[li]<=s[ri]) a[i++] = s[li++]; else { a[i++] = s[ri++]; ans+=(mid-l+1-li+l); } while(li<=mid) a[i++] = s[li++]; while(ri<=r) a[i++] = s[ri++]; for(int j = 1; j <= len; ++j) s[j+l-1] = a[j]; delete a; } void merge(int l, int r) { if(l>=r) return; int mid = l+(r-l)/2; merge(l,mid); merge(mid+1,r); move(l, mid, r); } int main () { while(scanf("%d",&n), n) { for(int i = 1; i <= n; ++i) scanf("%d",&s[i]); ans = 0; merge(1,n); printf("%I64d\n",ans); } return 0; }
树状数组:
如果第一次接触树状数组的,可以看参考http://blog.csdn.net/q573290534/article/details/6664902
离散化+树状数组,====>求逆序数。
思路:把离散化的数组【9 1 0 5 4】==>【 5 2 1 4 3】逆序更新状态即可,
例如
1、查找以3开头的逆序数,把 就是查找已经插入的比3小的所有数字的个数。然后把3插入到数组【0】
2、查找以4开头的逆序数,更新4【1】
3、查找以1开头的逆序数,更新1【0】
4、。。。2。。。。。【1】
5、。。。5。。。。。【4】
最后答案就是【1+1+4 = 6 】
代码如下:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define M 500005 #define lowbit(x) x&-x struct Node{ int v, x; }; Node a[M]; int n, r[M], tree[M]; int comp(const Node p, const Node q) { return p.v<q.v; } void update(int x) { while(x<=n) { tree[x]+=1; x+=lowbit(x); } } int sum(int x) { int ret = 0; while(x>0) { ret+=tree[x]; x-=lowbit(x); } return ret; } int main () { int t; while(scanf("%d",&n), n) { for(int i = 1; i <= n; ++i) { scanf("%d",&t); a[i].v = t; a[i].x = i; } sort(a+1,a+1+n,comp); for(int i = 1; i <= n; ++i) r[a[i].x] = i; long long ans = 0; memset(tree,0,sizeof(tree)); for(int i = n; i >= 1; i--) { ans+=sum(r[i]); update(r[i]); } printf("%I64d\n",ans); } return 0; }