nyoj 143 第几是谁?(逆康托展开)

描述

现在有"abcdefghijkl”12个字符,将其按字典序排列,如果给出任意一种排列,我们能说出这个排列在所有的排列中是第几小的。但是现在我们给出它是第几小,需要你求出它所代表的序列.

输入

第一行有一个整数n(0<n<=10000);
随后有n行,每行是一个整数m,它代表着序列的第几小;

输出

输出一个序列,占一行,代表着第m小的序列。

样例输入

3
1
302715242
260726926

样例输出

abcdefghijkl
hgebkflacdji
gfkedhjblcia
解题思路:逆康托展开:就是根据某个排列的在所有全排列中的名次来确定这个排列。比如求1234的所有排列中排第20的排列是啥,那么就利用辗转相除法确定康托展开中每一项的系数k,然后每次输出当前未出现过的第k个元素即可。
下面演示一下计算过程:因为康托展开值是统计比第k个排列小前面所有排列的个数,所以求第20个排列对应的康托展开值应减1-->为19。
用19/3!=3...余1,说明首元素排在未出现元素的第3个(从下标0开始算),即首位为4;
用1/2!=0...余1,说明第二个元素排在未出现元素的第0个,即第二个元素为1;
用1/1!=1...余0,说明第三个元素排在未出现元素的第1个,即第三个元素为3;
最后一位自然就是2,所以1234的所有排列中排第20位的排列为4132。
AC代码:
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 char str[13];LL n,m,pos,j,k,len,tmp,fac[13];bool flag[13];
 5 LL getfac(int n){//打印阶乘表
 6     if(n==0)return 1;
 7     LL ans=1;
 8     for(int i=1;i<=n;++i)ans*=i;
 9     return ans;
10 }
11 int main(){
12     for(int i=0;i<13;++i)fac[i]=getfac(i);//阶乘表
13     while(cin>>n){
14         while(n--){
15             cin>>m;k=0;len=13;m--;//m减掉次序1才是第m个排列的康托展开值
16             memset(flag,false,sizeof(flag));//初始化为未标记状态
17             for(LL i=1;i<len;++i){
18                 tmp=m/fac[len-i-1];
19                 for(j=0;j<len-1;++j){//遍历12个字符,找到还未出现的字母序列中的第tmp个
20                     if(!flag[j]){
21                         if(tmp==0)break;
22                         tmp--;
23                     }
24                 }
25                 str[k++]='a'+j;
26                 flag[j]=true;//标记第tmp个元素已出现
27                 m%=fac[len-i-1];
28             }
29             str[len-1]='\0';
30             cout<<str<<endl;
31         }
32     }
33     return 0;
34 }

 

posted @ 2018-08-17 16:25  霜雪千年  阅读(214)  评论(0编辑  收藏  举报