Poj 2299 Ultra-QuickSort 求逆序数 4种方法总结

来源:互联网 发布:隐藏电话号码软件 编辑:程序博客网 时间:2024/05/11 23:56

题目链接:http://poj.org/problem?id=2299

题意:给出长度为n的序列,每次只能交换相邻的两个元素,问至少要交换几次才使得该序列为递增序列。

思路:由线性代数知识,本题就是求一个数列的逆序数,逆序数较大需要__int64。总结目前接触到的四种方法:


方法一:归并排序

代码并非全部原创,但是参考自哪里我已经忘了……

#include <cstdio>#include <cstring>const int N=500005;int n;__int64 num[N],temp[N];__int64 back;void merg (int left,int mid,int right){__int64 i=left,j=mid+1,p=0;while (i<=mid && j<=right){if (num[j]<num[i]){temp[p++]=num[j++];back+=mid-i+1;}elsetemp[p++]=num[i++];}while (i<=mid)temp[p++]=num[i++];while (j<=right)temp[p++]=num[j++];for (i=0,j=left;i<p;i++,j++)num[j]=temp[i];}void mergsort (int left,int right){if (left<right){int mid=(left+right)>>1;mergsort(left,mid);mergsort(mid+1,right);merg(left,mid,right);}}int main (){while (~scanf("%I64d",&n),n){for (int i=0;i<n;i++)scanf("%I64d",&num[i]);back=0;mergsort(0,n-1);printf("%I64d\n",back);}return 0;}


接下来的两种方法需要用到离散化,简单总结一下:

struct Dis{int v;  //存储原数据int id;bool operator < (const Dis& b) const{return v<b.v;}}dis[N];  //离散化数组int data[N];  //离散化之后的数据,大小关系与输入时一样    for (i=1;i<=n;i++){scanf("%d",&dis[i].v);dis[i].id=i;}sort (dis+1,dis+n+1);for (i=1;i<=n;i++)data[dis[i].id]=i;

方法二:树状数组

自己想到的第一种方法

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;#define max(a,b) ((a)>(b)?(a):(b))const int N=500000;struct Node{int v;int id;bool operator < (const Node& b) const{return v<b.v;}}dis[N];  //离散化数组int bit[N],data[N];int n; int lowbit (int x){return x&(-x);}void Update (int k,int add){while (k<=n)  //是<=n{bit[k]+=add;k+=lowbit(k);}}int Cal (int k){int sum=0;while(k>0){sum+=bit[k];k-=lowbit(k);}return sum;}int main (){int i;while (~scanf("%d",&n) ,n){__int64 sum=0;for (i=1;i<=n;i++){scanf("%d",&dis[i].v);dis[i].id=i;}sort (dis+1,dis+n+1);for (i=1;i<=n;i++)data[dis[i].id]=i;memset(bit,0,sizeof(bit));for (i=1;i<=n;i++){Update(data[i],1);sum+=i-Cal(data[i]);}printf("%I64d\n",sum);}return 0;}

第三种方法:线段树

想到的第二种方法,感觉效率不如树状数组高

#include <cstdio>#include <iostream>#include <cstring>#include <algorithm>using namespace std;const int N=500005;struct Dis{int v;int id;bool operator < (const Dis& b) const{return v<b.v;}}dis[N];  //离散化数组int data[N];struct Node{int left,right;int num; //表示该区间已经出现节点的个数}a[N*4];void Build (int t,int left,int right){a[t].left=left;a[t].right=right;a[t].num=0;if (left==right)return;int mid=(left+right)>>1;Build(t*2,left,mid);Build(t*2+1,mid+1,right);}void Update (int t,int target){a[t].num++;if (a[t].left==target && a[t].right==target)return;int mid=(a[t].left+a[t].right)>>1;if (target<=mid)Update(2*t,target);elseUpdate(2*t+1,target);}int Query (int t, int left,int right){if (left>right)return 0;if (a[t].left==left && a[t].right==right)return a[t].num;int mid=(a[t].left+a[t].right)>>1;if (right<=mid)return Query(2*t,left,right);else if (left>mid)return Query(2*t+1,left,right);elsereturn Query(2*t,left,mid)+Query(2*t+1,mid+1,right);}int main (){#ifdef ONLINE_JUDGE#elsefreopen("read.txt","r",stdin);#endifint i,n;while (~scanf("%d",&n) ,n){__int64 sum=0;Build (1,1,n);for (i=1;i<=n;i++){scanf("%d",&dis[i].v);dis[i].id=i;}sort (dis+1,dis+n+1);for (i=1;i<=n;i++)data[dis[i].id]=i;for (i=1;i<=n;i++){Update (1,data[i]);sum+=Query(1,data[i]+1,n);  //查找比data[i]之前出现且比data[i]大的数}printf("%I64d\n",sum);}return 0;}

第四种方法:点树

网上偶然看到的方法,在此记录,数组貌似需要开到4倍才行。

现在还没有完全理解


点树的简单实现(极省空间) - flyinghearts - 博客园
http://www.cnblogs.com/flyinghearts/archive/2011/03/31/2001629.html

线段树的一种简化实现[原] by 踏雪赤兔 - 尘封阁 - IT博客
http://www.cnitblog.com/cockerel/archive/2006/09/13/16806.html

#include <cstdio>#include <cstring>const int N=1<<20;  // 表示可用区间为[0,N),其中N必须是2的幂数class PointTree{private:int a[2*N];int size;public:void Clear () { memset(this,0,sizeof(*this)); }void Add (int n){int i=N+n;size++;for (a[i]++;i>1;i/=2)if (~i&1)a[i/2]++;}int cntLs (int n){//统计小于n的数的个数int i=N+n,c=0;//若统计小于等于则c=a[i];for (;i>1;i/=2)if (i&1)c+=a[i/2];return c;}int cntGt (int n) { return size-a[N+n]-cntLs(n); }  //统计大于int cntEQ (int n) { return a[N+n]; }  //点集中n的个数void del (int n){if (!a[n+=N]) return ;size--;for (a[n]--;n>1;n/=2)if (~n&1)--a[n/2];}/*  解决:求点集中第i小的数(由0数起)     *  注意:如果i>=size 返回N-1     */ int operator [] (int n){int i=1;while (i<N){if (n<a[i]) i*=2;else n-=a[i], i=i*2+1;}return i-N;}}ob;int n;int main (){while (~scanf("%d",&n),n){int temp;__int64 sum=0;ob.Clear();for (int i=0;i<n;i++){scanf("%d",&temp);ob.Add (temp);sum+=ob.cntGt (temp);}printf("%I64d\n",sum);}return 0;}



原创粉丝点击