首先,关于什么是全排列不做解释。如果一个排列为A,下一个排列为A_NEXT,那么A_NEXT一定与A有尽可能长的公共前缀。 看具体例子,一个排列为124653,如何找到它的下一个排列,因为下一个排列一定与124653有尽可能长的前缀,所以,脑洞大开一下,从后面往前看这个序列,如果后面的若干个数字有下一个排列,问题就得到了解决。 第一步:找最后面1个数字的下一个全排列。 124653,显然最后1个数字3不具有下一个全排列。 第二步:找最后面2个数字的下一个全排列。 124653,显然最后2个数字53不具有下一个全排列。 第三步:找最后面3个数字的下一个全排列。 124653,显然最后3个数字653不具有下一个全排列。 ------插曲:到这里相信大家已经看出来,如果一个序列是递减的,那么它不具有下一个排列。 第四步:找最后面4个数字的下一个全排列。 124653,我们发现显然最后4个数字4653具有下一个全排列。因为它不是递减的,例如6453,5643这些排列都在4653的后面。 我们总结上面的操作,并总结出重复上面操作的两种终止情况: 1:从后向前比较相邻的两个元素,直到前一个元素小于后一个元素,停止 2:如果已经没有了前一个元素,则说明这个排列是递减的,所以这个排列是没有下一个排列的。 124653这个排列终止情况是上面介绍的第一种,从后向前比较相邻的2个元素,遇到4<6的情况停止。 并且我们可以知道: 1:124653和它的下一个排列的公共前缀为12(因为4653存在下一个排列,所以前面的数字12保持不变) 2:4后面的元素是递减的(上面介绍的终止条件是前一个元素小于后一个元素,这里是4<6) 现在,我们开始考虑如何找到4653的下个排列,首先明确4后面的几个数字中至少有一个大于4. 4肯定要和653这3个数字中大于4的数字中(6,5)的某一个进行交换。这里就是4要和6,5中的某一个交换,很明显要和5交换,如果找到这样的元素呢,因为我们知道4后面的元素是递减的,所以在653中从后面往前查找,找到第一个大于4的数字,这就是需要和4进行交换的数字。这里我们找到了5,交换之后得到的临时序列为5643.,交换后得到的643也是一个递减序列。 所以得到的4653的下一个临时序列为5643,但是既然前面数字变大了(4653--->5643),后面的自然要变为升序才行,变换5643得到5346. 所以124653的下一个序列为125643.
设P是1~n的一个全排列:p=p1p2......pn=p1p2......pj-1pjpj+1......pk-1pkpk+1......pn 1)从排列的右端开始,找出第一个比右边数字小的数字的序号j(j从左端开始计算),即 j=max{i|pi<pi+1} 2)在pj的右边的数字中,找出所有比pj大的数中最小的数字pk,即 k=max{i|pi>pj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者) 3)对换pi,pk 4)再将pj+1......pk-1pkpk+1pn倒转得到排列p'=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个下一个排列。
</pre><pre name="code" class="cpp">#include <iostream> #include <stdio.h> #include <algorithm> #define MAX 1000+5 using namespace std; int num,n,m,A[MAX]; void init() { for(int i=0;i<n;i++)A[i]=i+1; } int f() { for(int i=n-1;i>0;i--) { if(A[i-1]<A[i])return i-1; } return -1; } void print_permutation() { while(--m) { int k=f(); for(int i=n-1;i>k;i--) { if(A[i]>A[k]) { int temp=A[k]; A[k]=A[i]; A[i]=temp; break; } } sort(A+k+1,A+n); } } int main() { //freopen("t.txt","r",stdin); while(scanf("%d%d",&n,&m)!=EOF) { init(); print_permutation(); for(int i=0;i<n-1;i++)printf("%d ",A[i]); printf("%d\n",A[n-1]); } return 0; }