Minimum Inversion Number
Time limit: 1 Seconds Memory limit: 32768K
Total Submit: 1759 Accepted Submit: 855
Total Submit: 1759 Accepted Submit: 855
The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.
For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:
a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)
You are asked to write a program to find the minimum inversion number out of the above sequences.
For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:
a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)
You are asked to write a program to find the minimum inversion number out of the above sequences.
Input
The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.
Output
For each case, output the minimum inversion number on a single line.
Sample Input
10
1 3 6 9 0 8 5 7 4 2
1 3 6 9 0 8 5 7 4 2
Sample Output
16
解答方案
该问题关键在于相邻的两个排列之间逆序序列的个数之间的关系。假设一个排列为ai,ai+1,..aj。下一个排列为ai+1,..aj,ai,容易看出两个排列之间逆序个数之间的变化主要是因为ai位置移动造成的,设comp[i]表示排列中大于ai的数的个数,当ai移动到排列的末尾后,逆序个数增加comp[i]。但是需要注意原来排列中比ai小的数与ai构成逆序,当ai移动到排列的末尾后,原来这种逆序关系不再存在,因此排列的逆序个数需要同时减少n-comp[i],其中n为排列中元素的个数。扫描完所有排列,其中的最小逆序个数即为所求解。
Code:
#include <stdio.h>
#include <string.h>
#include <string.h>
#define MAXN 5000
inline int minFunc(int ,int );
int main(int argc,char **argv)
{
int seq[MAXN];
int comp[MAXN];
int i,j,n,cur;
int minValue;
{
int seq[MAXN];
int comp[MAXN];
int i,j,n,cur;
int minValue;
while(scanf("%d",&n) != EOF)
{
memset(comp,0,sizeof(comp));
minValue = 0;
{
memset(comp,0,sizeof(comp));
minValue = 0;
for(i=0;i<n;i++)
{
scanf("%d",&seq[i]);
for(j=0;j<i;j++)
{
if(seq[j]>seq[i])
{
comp[i]++;
minValue ++;
}
else if(seq[j]<seq[i])
{
comp[j]++;
}
}
}
{
scanf("%d",&seq[i]);
for(j=0;j<i;j++)
{
if(seq[j]>seq[i])
{
comp[i]++;
minValue ++;
}
else if(seq[j]<seq[i])
{
comp[j]++;
}
}
}
cur = minValue;
//cindex = n-1;
//printf("cur = %d/n",cur);
//cindex = n-1;
//printf("cur = %d/n",cur);
for(i=0;i<n-1;i++)
{
cur = cur+comp[i]-(n-1-comp[i]);
minValue = minFunc(minValue,cur);
//printf("comp[%d]=%d/n",i,comp[i]);
/*
if(cur<minValue)
{
minValue = cur;
cindex = i;
}
*/
}
{
cur = cur+comp[i]-(n-1-comp[i]);
minValue = minFunc(minValue,cur);
//printf("comp[%d]=%d/n",i,comp[i]);
/*
if(cur<minValue)
{
minValue = cur;
cindex = i;
}
*/
}
printf("%d/n",minValue);
/*
for(i=0;i<n;i++)
printf("%d ",seq[(i+cindex+1)%n]);
printf("/n");
*/
}
for(i=0;i<n;i++)
printf("%d ",seq[(i+cindex+1)%n]);
printf("/n");
*/
}
return 0;
}
}
inline int minFunc(int a,int b)
{
return a<b?a:b;
}
{
return a<b?a:b;
}
该算法的复杂度为O(n^2),主要是因为在计算序列中下一个元素的comp时候,需要调节前面元素的comp。改进方法是采用二分排序使得计算的复杂度降为O(nlogn),可以采用二叉排序树来实现。
算法改进:
结合堆排序和二叉排序树对算法进行了改进.代码长度增加了不少,但是运行时间降低了很多。主要改变是对整个序列用堆排序求出每个元素在序列中大于它的元素的个数,
用二叉排序树求初始序列中逆序的个数.下面的步骤跟原来的算法一样,采用递推求每个序列中逆序的个数. (正好趁这个机会实现了堆排序,函数接口仿照qsort.二叉排序树求初始序列个数逆序个数的思想请参见另外一个帖子:求序列中逆序的个数)
Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#define MAXN 5000
inline int minFunc(int ,int );
typedef struct
{
int num;
int val;
}inversionElem;
{
int num;
int val;
}inversionElem;
typedef struct
{
int val;
int lchild,rchild;
int lsize,rsize;
int repeat;
}treeNode;
{
int val;
int lchild,rchild;
int lsize,rsize;
int repeat;
}treeNode;
/*采用二叉排序树*/
int getInversionNum_BinarySortedTree(int [], int );
static bool find(treeNode [],int ,int &,int ,int & );
static int insert(treeNode [],int &, int, int& );
static void heapSort(void * , int , int , int (*)(const void *, const void *) );
static void sift(void * , int , int , int , int (*)(const void *, const void *) , void * );
static int compare(const void *, const void * );
int getInversionNum_BinarySortedTree(int [], int );
static bool find(treeNode [],int ,int &,int ,int & );
static int insert(treeNode [],int &, int, int& );
static void heapSort(void * , int , int , int (*)(const void *, const void *) );
static void sift(void * , int , int , int , int (*)(const void *, const void *) , void * );
static int compare(const void *, const void * );
int main(int argc,char **argv)
{
int seq[MAXN];
int comp[MAXN];
inversionElem heap[MAXN];
int i,j,n,cur,cindex;
int minValue;
{
int seq[MAXN];
int comp[MAXN];
inversionElem heap[MAXN];
int i,j,n,cur,cindex;
int minValue;
while(scanf("%d",&n) != EOF)
{
minValue = 0;
{
minValue = 0;
for(i=0;i<n;i++)
{
scanf("%d",&seq[i]);
heap[i].val = seq[i];
heap[i].num = i;
comp[i] = 0;
}
{
scanf("%d",&seq[i]);
heap[i].val = seq[i];
heap[i].num = i;
comp[i] = 0;
}
heapSort(heap,n,sizeof(inversionElem),compare);
/*
for(i=0;i<n;i++)
printf("%d ",heap[i].val);
printf("/n");
for(i=0;i<n;i++)
printf("%d ",heap[i].num);
printf("/n");
*/
for(i=0;i<n;i++)
printf("%d ",heap[i].val);
printf("/n");
for(i=0;i<n;i++)
printf("%d ",heap[i].num);
printf("/n");
*/
int pre = -1 ;
for(i=0;i<n;i++)
{
if(i>0&&heap[i-1].val==heap[i].val)
j = pre;
else
j = pre +1;
pre = j;
comp[heap[i].num] = j;
}
for(i=0;i<n;i++)
{
if(i>0&&heap[i-1].val==heap[i].val)
j = pre;
else
j = pre +1;
pre = j;
comp[heap[i].num] = j;
}
minValue = getInversionNum_BinarySortedTree(seq,n);
/*
for(i=0;i<n;i++)
{
scanf("%d",&temp);
int low = 0, hig = i-1;
while(low<=hig)
{
int mid = (low+hig)>>1;
if(heap[mid]<=temp)
low = mid + 1;
else
hig = mid -1 ;
}
for(j=hig+1;j<i;j++)
{
heap[j+1] = heap[j];
order[j+1] = order[j];
}
heap[hig+1] = temp;
order[hig+1] = i;
comp[i] = i - 1 - hig;
minValue +=comp[i];
for(j=hig;j>=0;j--)
if(heap[j]!=temp)
comp[order[j]]++;
}
*/
/*
for(i=0;i<n;i++)
{
scanf("%d",&temp);
int low = 0, hig = i-1;
while(low<=hig)
{
int mid = (low+hig)>>1;
if(heap[mid]<=temp)
low = mid + 1;
else
hig = mid -1 ;
}
for(j=hig+1;j<i;j++)
{
heap[j+1] = heap[j];
order[j+1] = order[j];
}
heap[hig+1] = temp;
order[hig+1] = i;
comp[i] = i - 1 - hig;
minValue +=comp[i];
for(j=hig;j>=0;j--)
if(heap[j]!=temp)
comp[order[j]]++;
}
*/
/*
for(i=0;i<n;i++)
printf("comp[%d]=%d/n",i,comp[i]);
*/
for(i=0;i<n;i++)
printf("comp[%d]=%d/n",i,comp[i]);
*/
cur = minValue;
cindex = n-1;
//printf("cur = %d/n",cur);
cindex = n-1;
//printf("cur = %d/n",cur);
for(i=0;i<n-1;i++)
{
cur = cur+comp[i]-(n-1-comp[i]);
//printf("comp[%d]=%d/n",i,comp[i]);
if(cur<minValue)
{
minValue = cur;
cindex = i;
}
//minValue = minFunc(minValue,cur);
//printf("cur = %d/n",cur);
}
{
cur = cur+comp[i]-(n-1-comp[i]);
//printf("comp[%d]=%d/n",i,comp[i]);
if(cur<minValue)
{
minValue = cur;
cindex = i;
}
//minValue = minFunc(minValue,cur);
//printf("cur = %d/n",cur);
}
printf("%d/n",minValue);
/*
for(i=0;i<n;i++)
printf("%d ",seq[(i+cindex+1)%n]);
printf("/n");
*/
}
for(i=0;i<n;i++)
printf("%d ",seq[(i+cindex+1)%n]);
printf("/n");
*/
}
return 0;
}
}
static void heapSort(void * arr, int nElem, int nsize, int (*f)(const void *, const void *))
{
int i;
void *temp = malloc(nsize);
{
int i;
void *temp = malloc(nsize);
for(i=nElem>>1;i>0;i--)
sift(arr,i-1,nElem-1,nsize,f,temp/* 重复利用动态分配内存*/); //arr[i-1..nElem-1]
for(i=nElem-1;i>=1;i--)
{
memcpy(temp,((char *)arr)+i*nsize,nsize);
memcpy(((char *)arr)+i*nsize,((char *)arr),nsize);
memcpy(((char *)arr),temp,nsize);
sift(arr,0,i-1,nsize,f,temp);
}
sift(arr,i-1,nElem-1,nsize,f,temp/* 重复利用动态分配内存*/); //arr[i-1..nElem-1]
for(i=nElem-1;i>=1;i--)
{
memcpy(temp,((char *)arr)+i*nsize,nsize);
memcpy(((char *)arr)+i*nsize,((char *)arr),nsize);
memcpy(((char *)arr),temp,nsize);
sift(arr,0,i-1,nsize,f,temp);
}
free(temp);
}
}
static void sift(void *arr,int left, int right, int nsize,int (*f)(const void *,const void *),void *temp)
{
/*
* arr[left+1..right]已经满足堆的性质,
* 加入arr[left]使得arr[left..right]满足堆的性质
*/
int i = left;
int j = (i<<1)+1;
bool finished = false;
{
/*
* arr[left+1..right]已经满足堆的性质,
* 加入arr[left]使得arr[left..right]满足堆的性质
*/
int i = left;
int j = (i<<1)+1;
bool finished = false;
memcpy(temp,((char *)arr)+left*nsize,nsize);
while(j<=right&&!finished)
{
if(j<right&&f((void *)(((char *)arr)+j*nsize),(void *)(((char *)arr)+(j+1)*nsize))>0)
j++;
if(f(temp,(void *)(((char *)arr)+j*nsize))<=0)
finished = true;
else
{
memcpy(((char *)arr)+i*nsize,((char *)arr)+j*nsize,nsize);
i = j;
j<<=1;
j++;
}
}
{
if(j<right&&f((void *)(((char *)arr)+j*nsize),(void *)(((char *)arr)+(j+1)*nsize))>0)
j++;
if(f(temp,(void *)(((char *)arr)+j*nsize))<=0)
finished = true;
else
{
memcpy(((char *)arr)+i*nsize,((char *)arr)+j*nsize,nsize);
i = j;
j<<=1;
j++;
}
}
memcpy(((char *)arr)+i*nsize,temp,nsize);
}
}
static int compare(const void *a, const void *b)
{
return ((inversionElem *)a)->val-((inversionElem *)b)->val;
}
{
return ((inversionElem *)a)->val-((inversionElem *)b)->val;
}
/*采用二叉排序树*/
int getInversionNum_BinarySortedTree(int a[], int n)
{
int root,inversionNum,tlen;
treeNode T[MAXN];
int getInversionNum_BinarySortedTree(int a[], int n)
{
int root,inversionNum,tlen;
treeNode T[MAXN];
root = tlen = -1;
inversionNum = 0;
inversionNum = 0;
for(int i= 0;i < n;i++)
{
inversionNum += insert(T,root,a[i],tlen);
}
{
inversionNum += insert(T,root,a[i],tlen);
}
return inversionNum;
}
}
static bool find(treeNode T[],int root,int &pre,int val,int &r)
{
bool found;
{
bool found;
if(root==-1)
{
found = false;
}
else
{
pre = root;
if(T[root].val>val)
{
T[root].lsize++;
r+=T[root].repeat;
r+=T[root].rsize;
found = find(T,T[root].lchild,pre,val,r);
}else if(T[root].val<val)
{
T[root].rsize++;
found = find(T,T[root].rchild,pre,val,r);
}else
{
found = true;
T[root].repeat++;
}
}
{
found = false;
}
else
{
pre = root;
if(T[root].val>val)
{
T[root].lsize++;
r+=T[root].repeat;
r+=T[root].rsize;
found = find(T,T[root].lchild,pre,val,r);
}else if(T[root].val<val)
{
T[root].rsize++;
found = find(T,T[root].rchild,pre,val,r);
}else
{
found = true;
T[root].repeat++;
}
}
return found;
}
}
static int insert(treeNode T[],int &root,int val, int &tlen)
{
int inversionNum = 0;
int pre;
bool found = find(T,root,pre,val,inversionNum);
if(!found)
{
tlen ++;
T[tlen].val = val;
T[tlen].lchild = T[tlen].rchild = -1;
T[tlen].lsize = T[tlen].rsize = 0;
T[tlen].repeat = 1;
{
int inversionNum = 0;
int pre;
bool found = find(T,root,pre,val,inversionNum);
if(!found)
{
tlen ++;
T[tlen].val = val;
T[tlen].lchild = T[tlen].rchild = -1;
T[tlen].lsize = T[tlen].rsize = 0;
T[tlen].repeat = 1;
if(root==-1)
{
root = 0;
}else
{
if(val<T[pre].val)
{
T[pre].lchild = tlen;
}else
{
T[pre].rchild = tlen;
}
}
}
{
root = 0;
}else
{
if(val<T[pre].val)
{
T[pre].lchild = tlen;
}else
{
T[pre].rchild = tlen;
}
}
}
return inversionNum;
}
}
/*采用二叉排序树*/
/*
inline int minFunc(int a,int b)
{
return a<b?a:b;
}
*/
inline int minFunc(int a,int b)
{
return a<b?a:b;
}
*/
当然最好的方法是利用线段树来求解,现在正在寻求这个解法。
本文介绍了一个算法问题——寻找一组数字序列经过特定变换后的最小逆序数。通过分析相邻排列间逆序数的变化规律,提出了两种解决方案:一种是时间复杂度为O(n^2)的直接方法;另一种则是结合堆排序和二叉排序树改进后的算法。
781

被折叠的 条评论
为什么被折叠?



