一道ACM题,求逆序对问题

这是我代码:
加上了注释:提示运行异常,我快疯了,只想知道答案

#include<stdio.h>#include<stdlib.h>#include<math.h>#define MAX 1000000void func(char *A, int x, int y, int* cnt, char *T); //根据归并排序计算char data[MAX]; //存每组的数据char T[MAX]; //排序过程中的辅助数组int cun[100000]; //每组数据计算出的逆序对数int main(){ int i, j; int group; //组数
int n; //每组有n个数据
int cnt = 0; //每组数据的逆序对数
scanf("%d", &group); for (i = 0; i<group; i++){ scanf("%d", &n); for (j = 0; j<n; j++){ scanf("%c", &data[j]); //接受每组数据里面的每个数据 while (data[j] == '\n' || data[j] == ' '){ //当不是空格不是回车时接收
scanf("%c",&data[j]); //接受每组数据里面的每个数据 } } func(data, 0, n, &cnt, T); cun[i] = cnt % (int)(pow(10,9)+7); cnt = 0; } for (i = 0; i<group; i++){ if (i != group - 1){ printf("%d\n",cun[i]); } else{ printf("%d", cun[i]); } } system("pause"); return 0;}void func(char *A, int x, int y, int* cnt, char *T) { if (y - x > 1) { int m = x + (y - x) / 2; int p = x, q = m, i = x; func(A, x, m, cnt, T); func(A, m, y, cnt, T); while (p < m || q < y) { if (q >= y || (p < m && A[p] <= A[q])) T[i++] = A[p++]; else { T[i++] = A[q++]; *cnt += m - p; } } for (i = x; i < y; ++i) A[i] = T[i];
}
}

第1个回答  2015-05-26
 一. 预备知识

.

  这部分就是百度上一搜一大片的东西,不过还是强调一下。

.

  1. 全排列

    从n个不同元素中任取m(m≤n)个元素,按照一定的顺序排列起来,叫做从n个不同元素中取出m个元素的一个排列。当m=n时所有的排列情况叫n的全排列。[1]对于n的全排列,共有n!种情况。

  2. 逆序、逆序数和奇、偶排列

    在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。逆序数为偶数的排列称为偶排列;逆序数为奇数的排列称为奇排列。[2]

   例如,对于n=3的全排列:

全排列

123

231

312

132

213

321

逆序数

0

2

2

1

1

3

奇偶性





.

  二. 相关问题

.

  1. 给定一个排列,求它的逆序数。[3]

问题:给定一个排列,求它的逆序数是多少。
    分析:设 p1,p2,…,pn 为n的一个全排列,则其逆序数为t=t1+t2+…+tn=

    其中 ti为排在pi 前,且比pi 大的数的个数。

    这部分代码比较简单,此处略去。

.

  2. 根据逆序数推排列数。[4]

问题:给定一个n元排列,它的逆序数存在且唯一。那么反过来,已知一个n元排列的逆序数为m,
这样的n元排列有多少种?
  分析:我们用f(n,m)表示逆序数为m的n元排列的个数,则求满足条件的排列个数问题就转化为求f(n,m)的值的问题。

  为了得到最终结果,我们先研究f(n,m)的性质。可以得到以下几个命题:

  [命题1] 对任意n>=2且0<=m<=C(n,2)时f(n,m)>=1;当m>C(n,2)时,f(n,m)=0

    C(a,b)表示从a个元素中选出b个元素的选择种数

  证明:先证命题1的前半部分。

    (1)n=2时,m只可能是0或1.有f(2,0)=1,f(2,1)=1

    (2)假设n=k时命题成立,即有f(k,m)>=1(0<=m<=C(k,2))

    当n=k+1时,排列1,2,3,…,k+1满足逆序数为0,所以有f(k+1,0)>=1

    若m>=1,考虑某一逆序数为m-1的k元排列a1,a2,…,ak(由归纳假设知这个排列存在).现将k+1插入到a(k-1)和ak之间得到一个k+1元排列a1,a2,…,a(k-1),k+1,ak,这个排列的逆序数为m,所以f(k+1,m)>=1

    综上所述,对所有的0<=m<=C(n,2)有f(n,m)>=1

    再证命题的后半部分(比较显然)。

    因为对于一n元排列,若要使其逆序数达到最大,那么排列中任两个数都构成逆序,其逆序数为C(n,2),所以不可能存在一n元排列使其逆序数大于C(n,2)

  [命题2] f(n,m)=f(n,C(n,2)-m)

    证明:对于一个逆序数为m的n元排列a1,a2,…,an,n元排列an,a(n-1),…,a2,a1的逆序数为C(n,2)-m.同理,对于一个逆序数为C(n,2)-m的n元排列a1,a2,…,an,n元排列an,a(n-1),…,a2,a1的逆序数为m.

    于是,逆序数为m的n元排列的集合 和 逆序数为C(n,2)-m的n元排列的集合 是一一对应关系,所以f(n,m)=f(n,C(n,2)-m)

  [命题3] f(n+1,m)=f(n,m)+f(n,m-1)+…+f(n,m-n)

    先考虑由1,2,…,n组成的一个排列a1,a2,…,an,由于任一个由1,2,…,n,n+1构成的排列,可以看成将n+1加入的a1左边,或an的右边,或ai与a(i+1)之间(1<=i<=n-1)而形成的。又由于在n+1加入任一个位置之后,其前面没有比n+1大的数,其后面的数都比n+1小,而且加入n+1之后,使得对后面的每一个数而言,其前面比自身大的数的个数增加1,对n+1前面的数而言,其前面比自身大的个数不变。于是当选取n+1加入a1的左边时,排列a1,a2,…,an的逆序数变为m+n,当n+1加入ai与a(i+1)之间(1<=i<=n-1)时,排列a1,a2,…,an的逆序数变为m+n-i,当加入an的右边时,排列a1,a2,…,an的逆序数仍为m。

    于是,对于任一逆序数为m的n+1元排列,考虑n+1所处的位置。若n+1在第k位,则其余n个元素构成逆序数为m-n+k-1的n元排列,且对任一逆序数为m-n+k-1的n元排列而言,当n+1放在该排列的某位,使得在新排列中n+1处于第k位,那么新排列成为一逆序数为m的n+1元排列。

    于是,逆序数为m的n+1元排列的集合 和 逆序数为m的n元排列的集合 是一一对应关系。这样,对于一个n+1元排列,f(n,m)表示n+1排在最后的排列个数,f(n,m-1)表示n+1排在倒数第二位的排列的个数……,f(n,m-n)表示n+1排在首位的排列个数,且f(n+1,m)为这些排列之和。

    所以f(n+1,m)=f(n,m)+f(n,m-1)+…+f(n,m-n)(注:记b<0时,f(a,b)=0)

  [命题4] f(n,0)=f(n,C(n,2))=1

  [命题5] f(n,1)=f(n,C(n,2)-1)=n-1(n>1)

    证明:由命题3和命题4有

       f(n,1)=f(n-1,1)+f(n-1,0)=f(n-1,1)+1

    反复应用上式,根据f(2,1)=1,得到命题5成立

  [命题6] f(n,2)=f(n,C(n,2)-2)=C(n,2)-1(n>2)

    证明:由命题3,4,5有

      f(n,2)=f(n-1,2)+f(n-1,1)+f(n-1,0)=f(n-1,2)+n-1

    反复应用上式,根据f(2,2)=0,得到命题6成立

    同理,可证明

      f(n,3)=C(n,3)-C(n,2)-C(n,1) (n>3)

      f(n,4)=C(n,4)+2C(n,3)-C(n,1) (n>4)

      f(n,5)=C(n,5)+3C(n,4)+2C(n,3)-C(n,2)+1 (n>5)

      …

    我们可以无限递推下去,计算f(n,k)的时候要用到前面所推导出的f(n,1)~f(n,k-1)。但是却很难得到一个通项公式。这个问题就类似于用∑(k^t-(k-1)^t) 来求∑(k^(t-1)),每作下一项都要用到前面所有的结果。

    由于在命题3中我们得出了一个关于f(n,m)的递推公式,而且又知道了一些初始值。因此可以设计一个程序,输入n,m,输出f(n,m)。[5]

.  

  参考代码:

+ View Code
.
  3. 根据每个数的逆序数,然后求出原排列。[6]

复制代码
问题:对于一个集合S={1,2,3,...N}的任一排列a1、a2、a3、... aN,我们定义ai的逆序数为
∑(aj>ai | j<i),即排在ai前的所有比ai大的数的个数。我们把每个数的逆序数按下标排出就构
成排列a1、a2、a3、... aN的一个逆序排列。比如排列 3 1 2 的逆序排列为 1 1 0(从左往右
依次是1的逆序数、2的逆序数、3的逆序数)。现在我们给出一个排列的逆序排列,请求出原排列。[7]
复制代码
  分析:<developing…一种题解请参阅引用7>

.

  4. 给定逆序数,求满足此逆序数的最小排列。[8]

问题:对于n的全排列,给定一个正整数m,找到满足逆序数是m的最小的排列。例如,n=5,m=4,此
时满足逆序数为4最小的排列是1 3 5 4 2。
  分析:解决此问题,要先了解以下几个命题和定理。(注意,在证明中排列的大于、小于指两排列在全排列中位置的前后关系,小于即在前,大于反之)

    [定理1]对于n的全排列,在它完全倒序的时候(也就是n,n-1,…,2,1的时候)逆序数最多。

      证明:此处省略,可用反证法简单证明。

    [命题1]对于一个形如1,2,3,…,i-1,i,n,…i+1的排列q(如n=5时的1,2,5,4,3),即在数n前保证首项为1且严格以公差为1递增而数n之后排列任意的数列,

        (1)当数n之后是递减的时候q的逆序数最多,为t=C(n-i,2)。

        (2)排列q是出现逆序数为t的最小排列。

  证明:首先证明(1)。

   这个证明很容易。因为数列的前i+1项已经确定,所以整个数列的逆序数只和i+2项到n项有关。显然,由定理1,当第i+2项到第n项完全倒序的时候逆序数最大。比如说排列1,2,5,4,3就比1,2,5,3,4构成的逆序对多。

   现在证明(2)。

   假设存在一个排列p的逆序数为m且p小于q。我们知道,q的前i位已经是数n在当前位置的最小排列。也就是说,在q中无论是n还是n之后的任意一个数,将它与q中的1到i位的任意一个数更换位置后得到的排列一定大于q。比如,对于排列q:1,2,5,4,3,前两个数1、2的位置是不能变的,否则会大于q,比如1,3,5,4,2。

   所以我们只能从q的第i+1位到n位入手。由(1)可知,当n在第i+1位时,无论后面的数怎么排列,逆序数都无法超过m。所以p的数n一定在第i+2到第n位的任一位上。

   好的,我们假设数n在第i+2位上。在这种情况下不难发现,我们把原来n后面最大的数填补到第i+1位,这时的逆序数最多。比如对于1,2,5,4,3,我们把5移动到第4位,然后将4填补到第3位,排列就变成了1,2,4,5,3,显然这要比1,2,3,5,4优。

   我们分析一下这时p的逆序数。因为数n到了第i+2位,则由它构成的逆序数为n-i-2。同样,由第i+1位(也就是例子中的4,即n-1)构成的逆序数也是n-i-2。显然,由于保持了递减的特征,剩下的第i+3到第n位的逆序数为C(n-i-2,2)。(这里姑且认为C(1,2)=1)这时总的逆序数为

          t1 =C(n-i-2,2)+2*(n-i-2)

            =(n-i-2)(n-i-3)/2+2*(n-i-2)

            =(n-i-2)(n-i+1)/2.

  而q的逆序数

          t2=C(n-i,2)

           =(n-i)(n-i-1)/2.

   用t2-t1,得到定值1。也就是说,无论n取何值,t2总是大于t1。

   如果再将数n向右移动一位,这时我们只有保持前1到i+1位(即例子中的1,2,3,4)不变才能够保证之后的变换能够使逆序数最大(这个很显然,不特别证明)。然而,当前面的i+1位确定了以后,怎么变换后面的数字才能够使总逆序数最大呢?还是要原来n后面最大的数填补到第i+1位。我们已经证明,这样做是不如不换优的。所以,不存在一个小于q的排列p使p的逆序数为m。证毕。

     [命题2]在命题1所设定的排列q的基础上,我们将数n后面的第k小数与数n的前一个数(即i)交换,然后使数n后面保持逆序。这样得到的新排列所含的逆序数为t=C(n-i,2)+k,且这个排列是逆序数为t的最小排列。

证明:首先,在此强调一下排列q的结构,如图一:

   由于操作以后数n之后仍然保持逆序,所以从第i+1到第n位的逆序数仍为C(n-i,2)。对于x来说,交换之前后面有k-1个比它小的数,交换之后又加上了一个,所以x所拥有的逆序数(即以x为左的逆序数)为k-1+1=k,此时总的逆序数为C(n-i,2)+k。

   对于新排列逆序数为t=C(n-i,2)+k的最小排列的证明与命题1的证明相似,在此处不加以详细证明。

   到此我们可以得出,命题2也是真命题。

   有了这两个命题作基础,我们很容易就能够得到这道题的算法。首先利用命题1,二分查找找到数n的位置,我们可以得到数n在这个位置的时候形如q的排列的逆序数t。然后计算t与m的差值,得出k,然后根据命题2进行变换,最后得出要求的排列。

.

   参考代码:

+ View Code
第2个回答  推荐于2017-11-25
给个链接上去提交看下情况,

你写的太复杂了点,
明明是数字写成字符来处理不太好。本回答被网友采纳
相似回答