zz http://blog.csdn.net/rockics/article/details/6181464
问题描述如下:
一个具有n个元素的排列,经过k趟bubble sort排到有序状态。求出满足此条件的排列个数。
首先,了解反序表的概念。《计算机程序设计艺术》第三卷5.1.1反序部分有说明。简述如下:a1,a2,...,an是集合{1,2,...,n}的一个排列。令bj为位于j左边但是大于j的元素个数,就能得到排列a1,a2,...,an的反序表b1,b2,...,b3。比如说:排列
5 9 1 8 2 6 4 7 3
有反序表
2 3 6 4 0 2 2 1 0 。
不管什么样子的排列,只要可以比较大小,就可以映射到集合{1,2,...,n}上的一个排列。不难发现,每一趟bubble sort ,都会将这一趟之前反序数大于0的元素的反序数减1。经过k趟之后,所有元素的反序数均为0,也就是说,反序表为n个0,亦即有序状态。因此,题目可以演化为,n个元素的排列,反序表中最大值为k,求这样的排列有多少种。
易知,第i个元素的反序数取值范围是[0,n-i],还注意到一个事实,就是:每个元素的反序数取值是相互独立。为什么呢?事实上我开始以为是不独立的,但是验证了几个例子以后,我推翻自己的想法。当然这只是直觉上的。但是可以从《计算机程序设计艺术》第三卷反序部分得到一点理论支撑。引用书上的话“一张反序表唯一的确定一个排列”(我没有细看这个事实的证明)。因此,反序表和排列之间一一对应。全排列个数是n!,因此反序表的个数也是n!,这只有在取值相互独立的情况下得到。
回到题目,反序数最大为k。那么当n-i<=k,即i>=n-k的时候,元素i可随意放。因为不管怎么放,他们的反序数都不会大于k,取值的个数由i来决定。当i<n-k时候,这些元素的反序数只可以在[0,k]之间选择。这样,我把n个元素分为两类来讨论,前面一类有k+1个元素,后面一类有n-k-1个元素。先考虑前面一种情况,对于每一个i,易知其排列数有(n-i+1),从i=n-k到n连乘,就是(k+1)!。后面一类,每个元素都有(k+1)种选择,因此有(k+1)^(n-k-1)。由乘法定理,易得k!*(k+1)^(n-k)。
但是,这不是最终答案。上面的结果,只是说经过不超过k趟排到有序状态。并不符合题意。因此,必须保证有一个元素的反序数为k。最好的做法就是计算出不超过k-1趟就排到有序状态的排列数,减掉。很容易利用刚才的结论,得到不超过k-1趟就排到有序状态的排列数为:(k-1)!*(k)^(n-k+1)。两者想减,即得到结果:
k!*((k + 1)^(n - k) - k ^(n - k))。
最后排除异类的方法,是写这blog时候想到的。之前并不是这样做。而是去考虑一个一个剔除。想想因为之前那样的分类,对导致在剔除异类时候两类都必须考虑(第一类和第二类的反序数都有可能取得到k)。因此重新对之前的分类做了一下改变。事实上,就是把元素n-k从第一类中移到第二类。这样讨论起来特别方便。只需要考虑第二类的异类即可