康托展开

康托展开/逆展开,快速的求全排列中的某个排列

康拓展开:ans=a1*(n-1)!+a2*(n-2)!+......+an*(0)!;

ans表示n个数全排列中的第几个排列(从0开始查),ai表示这一位数在包括他和后面所有的数中排第几(从0开始)

//康托展开    f[]={1,1,2,6,24,120,720,5040,40320.......};
LL Work(char str[])
{
int len = strlen(str);
LL ans = 0;
for(int i=0; i<len; i++)
{
int tmp = 0;
for(int j=i+1; j<len; j++)
if(str[j] < str[i]) tmp++;                             //计算出后面有几个数小于str[i],即此位置的排序(上文的ai),再乘上对应的阶乘

ans += tmp * f[len-i-1]; //f[]为阶乘
}
return ans+1; //返回该字符串是全排列中第几大(第ans+1个排列),从1开始
}

逆展开,根据给出的ans打印出第ans个排列的形式


#include <stdio.h>
#include <cstring>
#include<iostream>
using namespace std;
int a[12] = { 1 , 2 , 6 , 24 , 120 , 720 , 5040 , 40320 , 362880 , 3628800 , 39916800 ,479001600} ;
int vis[1200] ;
int res[1200] ,len;
void kang(int m,int n)          //m位的第n个排列            
{
int i,j;
memset( vis , 0 , sizeof( vis ) ) ;      //标记使用过的数字
memset( res , 0 , sizeof( res ) ) ;      //记录ai的值

n -= 1 ;                                      //由于从0开始,所以就是第(n-1)个排列             
for( i = m-2 ; i >= 0 ; --i )            //只要计算出前(m-1)个位置的值,最后一个位置的值自然也就确定了
{
res[m-2-i] = n / a[i] ;                 //每次的ai就是让n/对应阶乘,因为是m个数。所以从(m-1)!开始
n %= a[i] ;
}
for( i = 0 ; i < m-1 ; ++i )
{

int c = res[i] ;
for( j = 0 ; j <= c ; ++j ) //关键
{
if( vis[j] )    c++ ;          //精髓,防止元素重复使用和与前面的元素冲突(前面如果存在比他大小的,他在所有元素中的排列就要往后退);
}
vis[c] = 1 ;
res[i] = c ;                  //此时的c即此位置的值在全排列元素中的位置(例如c=1,在abc中就代表b)

}
for( i = 0 ; i < m ; ++i )
{
if( !vis[i] )
res[m-1] =i ;
}
for( i = 0 ; i < m ; ++i ){
if (i>0||len>0) printf(" ");
printf("%c", res[i]+'a' ) ;
}
printf(" ") ;
}
int main()
{
int n , m;

while(scanf("%d%d", &m, &n )!=EOF )
{
kang(m,n);

}
return 0 ;
}

原文地址:https://www.cnblogs.com/zzqc/p/6403303.html