题目大意:
背景:给定一个数n,那么从 1-n有你n!中排列。即n个数的全排列。现在规定这 n! 种排列的顺序。如 给定数字3。那么它的按序全排列是:
1 2 3,
1 3 2,
2 1 3,
2 3 1,
3 1 2 ,
3 2 1
问题:先给定一个数字n,和它的其中一个排列,求这个排列之后的第k个排列。如给定:
n = 3. 排列: 2 1 3
k = 2,则2 1 3 之后的第2个排列应该是 3 1 2。
k = 4, 是 1 2 3
分析:
自己没想出来,这里是看的别人的分析,直接贴过来了就。 http://blog.csdn.net/visame/article/details/2455396
我使用了字典序法
字典序法中,对于数字1、2、3......n的排列,不同排列的先后关系是从左到右逐个比较对应的数字的先后来决定的。例如对于5个数字的排列 12354和12345,排列12345在前,排列12354在后。按照这样的规定,5个数字的所有的排列中最前面的是12345,最后面的是 54321。
字典序算法如下:
设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的下一个下一个排列。
举例如下:
839647521是数字1~9的一个排列。从它生成下一个排列的步骤如下:
1. 自右至左找出排列中第一个比右边数字小的数字4 839647521
2. 在该数字后的数字中找出比4大的数中最小的一个5 839647521
3. 将5与4交换 839657421
4. 将7421倒转 839651247
所以839647521的下一个排列是839651247。
个人C++代码如下:
#include <iostream>
using namespace std;
void PrintArr( int arr[] , int length);
void Reverse( int arr[] , int start , int end , int length);
void NextPermutation( int arr[],int length );
int main()
{
int caseNum;
int arr[1024];
while( cin>>caseNum )
{
if( caseNum <= 0 )
continue;
int i = 0;
int n =0;
int k = 0;
while( i < caseNum )
{
cin>>n>>k;
int j = 0;
while( j < n )
{
int tempj = 0;
cin>>arr[j];
j++;
}
//获取当前排列后第k个排列。
for( j = 0; j < k ; j++)
{
NextPermutation( arr , n );
}
PrintArr( arr, n );
i++;
}
}
return 0;
}
void PrintArr( int arr[] , int length)
{
for( int i = 0; i < length; i++ )
{
if( i!=0 )
cout<<" ";
cout<<arr[i];
}
cout<<endl;
}
void NextPermutation( int arr[] , int length )
{
if( arr == NULL || length <= 0 )
return;
int p = length - 1;
p--;
//从最右起,获取第一个小于自己右边的数。
while ( p >= 0 && arr[p] > arr[p+1] )
p--;
//特殊情况处理
if( p < 0 )
{
for( p = 0; p < length ; p++ )
arr[p] = p+1;
return;
}
int minIndex = p + 1;
int k = p+1;
//找右边大于arr[p]数字钟最小的数字。
while( k < length )
{
if( arr[p] < arr[k] && arr[k] < arr[minIndex] )
minIndex = k;
k++;
}
//交换
int temp = arr[p];
arr[p] = arr[minIndex];
arr[minIndex] = temp;
//逆序
Reverse( arr , p+1, length -1 ,length );
}
void Reverse( int arr[] , int start , int end , int length )
{
if( arr == NULL || start >= end || end >= length || length <= 0 )
return;
while ( start < end )
{
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}