1.康托展开(按照字典序即从小到大生成排列)
a) 康托展开的公式是 X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!其中,ai为当前未出现的元素中是排在第几个(从0开始)。
b) an为小于该元素的值有几个
i. 假设有”abcdefghijkl”12个字符(初始时为字典序)
1. 如hgebkflacdji为上述字符的一个排列
a) 所以a(12) = 7, a(11) = 6, a(10) = 4,1,6,3,5,0,0,0,1,0
b) ai(1 < i <= n)的值是怎么求的呢?如:a(12) = 7表示在hgebkflacdji排列中比h小的有7个:abcdefg;
a(11) = 6表示在gebkflacdji排列中比g小的有6个:abcdef;
a(10) = 4表示在ebkflacdji排列中比e小的有:abcd。 后面依次类推得出结果。
我排第几个(南理139题)
时间限制:1000 ms | 内存限制:65535 KB
难度:3
描述
现在有"abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的?
输入
第一行有一个整数n(0<n<=10000);
随后有n行,每行是一个排列;
输出
输出一个整数m,占一行,m表示排列是第几位;
样例输入
3
abcdefghijkl
hgebkflacdji
gfkedhjblcia
样例输出
1
302715242
260726926
#include<stdio.h>
char str[13];
long factorial(int n) {
long sum = 1;
for (int i = 2; i <= n; i++) sum *= i;
return sum;
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
int count;
long sum = 0;
int k = 11;
scanf("%s", str);
for (int i = 0; i < 12; i++) {
count = 0;
for (int j = i+1; j < 12; j++) {
if (str[i] > str[j]) count++;
}
sum += count*factorial(k);
k--;
}
printf("%ld\n", sum+1);
}
return 0;
}
2.康托展开的逆运算(可以生成全排列)
a) 求abcd的第20个排列
i. 20/(3!) = 3 余 2
ii. 2/(2!) = 1 余 0
iii. 0/(1!) = 0 余 0
iv. 0/(0!) = 0 余 0
b) 此排列为dbac
i. 有3个比它小的是d
ii. 有1个比它小的是b
iii. 有0个比它小的是a
iv. 有0个比它小的是c
c) 代码实现
代码1:
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
char str[10];
int len;
string s;
int factorial(int n) {
int sum = 1;
for (int i = 2; i <= n; i++) sum *= i;
return sum;
}
long position() {//求一个排列的位置
long pos;
int count;
vector<int> vec(10005);
for (int i = 0; i < len; i++) {
count = 0;
for (int j = i+1; j < len; j++)
if (str[i] > str[j]) count++;
vec[len-i-1] = count;
}
pos = 0;
for (int i = 0; i < len; i++)
pos += vec[i]*factorial(i);
return pos;
}
void print(int pos) {
vector<char> num;
for (int i = 0; i < len; i++) num.push_back(str[i]);
div_t divresult;
for (int i = len-1; i >= 0; i--) {
divresult = div(pos, factorial(i));//求余数与除数
s.push_back(num[divresult.quot]);
num.erase(num.begin() + divresult.quot);
pos = divresult.rem;//余数
}
}
int main() {
scanf("%s", &str);
len = strlen(str);
printf("%ld\n", position()+1);//输出一个排列的位置
print(302715241);//打印该位置的排列 位置序数从0开始
cout << s << endl;
return 0;
}
代码2:(此代码来自:http://blog.youkuaiyun.com/zhongkeli/article/details/6966805)
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdlib>
using namespace std;
class cantor {
public:
int n;//字符串的长度
string s;
int pos;//字符串在全排列中的字典位置,从0开始
vector<char> num;//所有的字符
cantor(string s):s(s) {n = s.size();}
cantor(int n, int pos):n(n), pos(pos) {
int i;
for (i = 0; i < n; i++)
num.push_back(i+65);
}
int fac(int);
void encode();
void decode();
};
int cantor::fac(int num) {
if (num == 0) return 1;
else return num * fac(num-1);
}
void cantor::encode() {
int i, j, count;
vector<int> vec(n);
for (i = 0; i < n; i++) {
count = 0;
for (j = i; j < n; j++)
if (s[i] > s[j]) count++;
vec[n-i-1] = count;
}
pos = 0;
for (i = 0; i < s.size(); i++)
pos += vec[i]*fac(i);
}
void cantor::decode() {
int i;
div_t divresult;
for (i = n-1; i >= 0; i--) {
divresult = div(pos, fac(i));//求余数与除数
s.push_back(num[divresult.quot]);
num.erase(num.begin() + divresult.quot);
pos = divresult.rem;//余数
}
}
int main(){
for (int i = 0; i < 24; i++) {
cantor test(4, i);
test.decode();
cout << test.s << endl;
}
}
1万+

被折叠的 条评论
为什么被折叠?



