LightOJ1060 nth Permutation(不重复全排列+逆康托展开)
一年多前遇到差不多的题目http://acm.fafu.edu.cn/problem.php?id=1427。
一开始我还用搜索。。后来那时意外找到一个不重复全排列的计算公式:M!/(N1!*N2!*...*Nn!),
然后就靠自己YY出解法,搞了好几天,最后向学长要了数据,然后迷迷糊糊调了,终于AC了。
后来才知道当时想的解法类似于逆康托展开,只是逆康托展开是对于没有重复元素全排列而言,不过有没有重复元素都一个样。
而现在做这题很顺,因为思路很清晰了,另外这做法和数论DP的统计部分有相似之处。
1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 long long fact[21]={1}; 5 long long calu(int n,int *cnt){ 6 long long res=fact[n]; 7 for(int i=0; i<26; ++i) res/=fact[cnt[i]]; 8 return res; 9 } 10 int main(){ 11 for(int i=1; i<21; ++i) fact[i]=fact[i-1]*i; 12 char str[22]; 13 long long n; 14 int t; 15 scanf("%d",&t); 16 for(int cse=1; cse<=t; ++cse){ 17 scanf("%s%lld",str,&n); 18 int sn=strlen(str),cnt[26]={0}; 19 for(int i=0; i<sn; ++i) ++cnt[str[i]-'a']; 20 if(calu(sn,cnt)<n){ 21 printf("Case %d: Impossible\n",cse); 22 continue; 23 } 24 printf("Case %d: ",cse); 25 for(int i=0; i<sn; ++i){ 26 for(int j=0; j<26; ++j){ 27 if(cnt[j]==0) continue; 28 --cnt[j]; 29 if(n>calu(sn-i-1,cnt)){ 30 n-=calu(sn-i-1,cnt); 31 ++cnt[j]; 32 }else{ 33 putchar(j+'a'); 34 break; 35 } 36 } 37 } 38 putchar('\n'); 39 } 40 return 0; 41 }