全排列与逆序对
在百度上搜索“全排列及其逆序数”能找出N多结果,但是内容很分散。本文旨在对这一方面的问题进行总结整理,如有不完整或者错误之处请与本人联系。
下面是正文。
.
一. 预备知识
.
这部分就是百度上一搜一大片的东西,不过还是强调一下。
.
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]
.
参考代码:
program code1; var a:array[1..500,1..500]of longint; b,c:longint; i,j,m,n,k:longint; begin a[2,0]:=1; a[2,1]:=1; for i:=3 to 500 do for j:=0 to 499 do begin b:=j-i; if b<0 then b:=0 else b:=j-i+1; for k:=n to j do c:=c+a[i-1,k]; a[i,j]:=c; c:=0; end; readln(n,m); writeln(a[n,m]); end.
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进行变换,最后得出要求的排列。
.
参考代码:
program code2; var m,n,p:int64; i,j:longint; function find(x:longint):longint; var l,r,mid,k1,k2:int64; begin l:=1; r:=n; while l<r do begin mid:=(l+r)>>1; k1:=((mid-1)*mid)div 2; k2:=(mid*(mid+1))div 2; if (x<k2)and(x>=k1)then exit(mid) else begin if x>k1 then l:=mid+1; if x<k1 then r:=mid; end; end; if l=r then exit(l); end; procedure make; var k:int64; i,j:longint; v:array[1..50000]of boolean; begin fillchar(v,sizeof(v),false); k:=m-((p*(p-1))div 2); for i:=1 to n-p-1 do begin write(i,' '); v[i]:=true; end; v[n-p+k]:=true; v[n]:=true; if n-p+k>0 then write(n-p+k,' '); write(n,' '); for i:=n downto 1 do if not v[i] then write(i,' '); end; begin readln(n,m); p:=find(m); make; end.
.
本文介绍了有关全排列和逆序对的4个问题,层次有限而且欠全面。希望已经有的部分对你能够有所帮助,如果有任何纰漏,请尽快回复本文或者与我联系,谢谢!
.
引用来源:
[1]:百度百科 词条:全排列
[2]:百度百科 词条:逆序数
[3]:题目来源:经典问题
[4]:题目来源:经典问题
[5]:charlesgao数学博客 http://www.charlesgao.com/?p=19
[6]:题目来源:boj1275构造原排列
[7]:joaquin的百度空间 http://hi.baidu.com/joaquin_shj/blog/item/77961aea310d09d6d539c988.html
[8]:题目来源:POJ某题(寻找中)/某模拟赛-末日传说
扩展阅读:
[1]:HDU 1394 http://acm.hdu.edu.cn/showproblem.php?pid=1394
相关题解:http://blog.163.com/prevBlogPerma.do?host=lyt9469@126&srl=170442358201122002733316&mode=prev
[2]:POJ 2828 http://poj.org/problem?id=2828
[3]:BOJ 1197
本文地址:http://www.cnblogs.com/saltless/archive/2011/06/01/2065619.html
(saltless原创,转载请注明出处)