https://vijos.org/p/1092
描述
输入两个自然数m,n 1<=n<=20,1<=m<=n!
输出n个数的第m种全排列。
如 :
输入 3 1
输出 1 2 3
格式
输入格式
在一行中输入n m
输出格式
一个数列,既n个数的第m种排列
每两个数之间空1格
样例1
样例输入1[复制] 3 2
样例输出1[复制] 1 3 2
code :
// 主要是用康托展开式逆运算算法
#include <iostream>
#include <string>
using namespace std;
const int PermSize = 21;
//将20进制的数全不打印出来
__int64 factory[PermSize] = { 0,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,6227020800,87178291200,1307674368000,20922789888000,355687428096000,6402373705728000,121645100408832000,2432902008176640000};
void Uncantor(int n, int k)
{
int i, j, vst[30]={0},s[30];
int t;
k--;
//vst 用来标记
for (i=0; i<n; i++)
{
if(k==0 || factory[n-i-1]==0) // 当其中一个为0是以是最后一次进行操作
t=0;
else
{
t = k/factory[n-i-1]; // 0/0 会出现编译出错
k %= factory[n-i-1];
}
for (j=1; j<=n; j++)
if (!vst[j]) //如果存在就要往后移
{
if (t == 0) break;
t--;
}
s[i] = j;
vst[j] = 1;
}
for(i=0;i<n-1;i++)
cout<<s[i]<<" ";
cout<<s[i]<<endl;
}
int main()
{
int n,m;
while(cin>>n>>m)
{
Uncantor(n,m);
}
return 0;
}
康托展开的逆运算:
{1,2,3,4,5}的全排列已经从小到大排序,要找出第16个数:
1. 首先用16-1得到15
2. 用15去除4! 得到0余15
3. 用15去除3! 得到2余3
4. 用3去除2! 得到1余1
5. 用1去除1! 得到1余0
有0个数比它小的数是1
所以第一位是1
有2个数比它小的数是3,但1已经在之前出现过了所以是4
有1个数比它小的数是2,但1已经在之前出现过了所以是3
有1个数比它小的数是2,但1,3,4都出现过了所以是5
最后一个数只能是2
所以这个数是14352