Light OJ 1060 - nth Permutation(组合数)
题目大意:
给你一个字符串,问这个字符串按照特定顺序排列之后,第n个字符串是哪个?
题目分析:
首先我们要会求解总个数。也就是共有len个字符,每个字符有ki个,那么总组合方式是多少种?
总组合方式就是: (len!)/(ki !), 把每个ki的阶乘都除一边,最后算出的结果就是答案了。
那么问题就剩下解决第n个字符串的的问题了。
下面我们先了解一下排序计数:
排序计数
- 如1,2,3,4的全排列,共有4!种,求第10个的排列是(从1计起)?
- 先试首位是1,后234有3!=6种<10,说明首位1偏小,问题转换成求2开头的第(10-6=4)个排列,而3!=6 >= 4,说明首位恰是2。
- 第二位先试1(1没用过),后面2!=2个<4,1偏小,换成3(2用过了)为第二位,待求序号也再减去2!,剩下2了。而此时2!>=2,说明第二位恰好是3。
- 第三位先试1,但后面1!<2,因此改用4。末位则是1了。
- 这样得出,第10个排列是2-3-4-1。
然后求解方法和上面阶乘的方法大同小异。
=============================================================================================================
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #include<vector> 8 #include<map> 9 using namespace std; 10 typedef long long LL; 11 const int INF = 1e9+7; 12 const int MAXN = 555; 13 int vis[30]; 14 char str[50]; 15 LL Fact[25], n; 16 17 void DFS(int pos,LL num) 18 { 19 if(pos == 0) 20 { 21 printf("\n"); 22 return ; 23 } 24 25 for(int i=0; i<26; i++) 26 { 27 if(vis[i] == 0) continue; 28 29 LL temp = Fact[pos-1]*vis[i]; 30 31 for(int j=0; j<26; j++) 32 temp /= Fact[vis[j]]; 33 if(num > temp) 34 num -= temp; 35 else 36 { 37 vis[i] --; 38 printf("%c", 'a'+i); 39 break; 40 } 41 42 } 43 DFS(pos-1, num); 44 } 45 46 47 int main() 48 { 49 int T, cas = 1; 50 Fact[0] = 1; 51 for(int i=1; i<=22; i++) 52 Fact[i] = Fact[i-1] * i; 53 scanf("%d", &T); 54 55 while(T --) 56 { 57 scanf("%s %lld",str, &n); 58 59 memset(vis, 0, sizeof(vis)); 60 int len = strlen(str); 61 for(int i=0; str[i]; i++) 62 vis[str[i] - 'a'] ++; 63 LL ans = Fact[len]; 64 65 printf("Case %d: ", cas ++); 66 for(int i=0; i<26; i++) 67 ans /= Fact[vis[i]]; 68 69 if(ans < n) 70 puts("Impossible"); 71 else 72 DFS(len, n); 73 } 74 return 0; 75 }