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
",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('
');
39     }
40     return 0;
41 }
原文地址:https://www.cnblogs.com/WABoss/p/5135911.html