nyoj 139 我排第几个(康托展开)
描述
现在有"abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的?
输入
第一行有一个整数n(0<n<=10000);
随后有n行,每行是一个排列;
输出
输出一个整数m,占一行,m表示排列是第几位;
样例输入
3 abcdefghijkl hgebkflacdji gfkedhjblcia
样例输出
1 302715242 260726926
解题思路:康托展开:已知一个排列,求这个排列在全排列中是第几个。康托展开表示的是当前排列在n个不同元素的全排列中的名次。比如213在这3个数所有排列中排第3。那么,对于n个数的排列,康托展开为:$ X = a_n \times (n - 1)\! + a_{n-1} \times (n-2)\! + \cdots + a_2 \times 1\! + a_1 \times 0\! $,
其中 $a_i $ 表示第i个元素在未出现的元素(从小到大排列,并从0开始数)中排第几,(即第i+1位~第n位的元素中,也就是求后面有几个元素比第i个元素小)举个栗子:对于排列4213来说,4在4213中排第3,注意下标从0开始,2在213中排第1,1在13中排第0,3在3中排第0,即:$a_4 = 3, a_3 = 1, a_2 = 0, a_1 = 0$,这样就得到4213在所有排列中排在第X+1=20+1=21个。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 string str;int n;LL sum,num,len,fac[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>>str;sum=0;len=str.size(); 16 for(LL i=0;i<len;++i){ 17 num=0; 18 for(LL j=i+1;j<len;++j) 19 if(str[i]>str[j])num++;//找到还没出现且比当前字母小的字母个数,即在未出现的字母从小到大排列中排第几,注意下标从0开始 20 sum+=num*fac[len-i-1];//累加康托展开每一项的值 21 } 22 cout<<sum+1<<endl;//小于str的排列数有sum个,那么str就排在第sum+1个 23 } 24 } 25 return 0; 26 }