题目来源:http://acm.nyist.net/JudgeOnline/problem.php?pid=139
康托展开
康托展开就是一个全排列到自然数的双映射,因此康托展开是可逆的,即存在逆映射。
x = a[n]*(n-1)! + a[n-1]*(n-2)!+······a[1]*0!;
x表示的就是所有序列中该序列所在的位置,其中a[n]表示是比第一个位置的字母或数字小的个数,那么其余位置是n-1个数的全排列,a[n-1]表示比第2个位置的字母或者数字小的个数,如果所得的个数中包含了第一个位置的字母或者数字,那么减去1就是a[n-1]·····文字太枯燥了,还是以例子讲述吧。
例如 arr = {4, 5, 6, 7, 8},这5个数那么其全体全排列,按从小到大的顺序排列后,第67854是第几位?
a[5] = 2, a[4] = 2(因为6已经在第一次出现了), a[3] = 2, a[2] = 1, a[1] = 0;则:
x = a[5]*4! + a[4]*3! + a[3]*2 + a[2]*1! + a[1]*0!;
逆康托展开;
就是给你一个函数值,找出它在定义域中的原像。
首先交代一下,以下多项式:
a1*x1+a2*x2+a3*x3+·····an*xn = sum;
那么求系数就可以:看成sum = an*xn + r(其中:r = a1*x1 + a2*x2 + ····an-1*xn-1)
则sum/xn就是an,而r = sum%xn;同理an-1 = r/xn-1;····以下类似就可以求每一个系数!
那么利用上述性质就可以求出康拓展开式中各项的系数。求出系数,利用系数的含义就可以打印出序列的内容!例如:
arr = {1, 2, 3, 4, 5}这五个数全排列后并按从小到大的顺序排列,那么第78个序列是什么?
首先78-1,减去本身;
77/4! = 3····3;该序列中第1个位置之后比第1个位置小的数有三个,在arr中找出满足条件的,第一位应该是4
3/3! = 0····3;该序列中第2个位置之后没有比第2个位置小的数,那么该位置就是1;
3/2! = 1 ····1;该序列中第3个位置之后比第3个位置小的数有1个,那么该位置就是3;
1/1! = 1·····0;····4········4······1,·········5;
0/0! = 0·····0;····5········5······0,·········2;
那么最后一位就是2,即:41352;
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 12;
int fac[MAXN];
void Inite()
{
int i;
fac[0] = fac[1] = 1;
for(i = 2; i < MAXN; ++i)
fac[i] = i * fac[i-1];
}
int main()
{
char str[MAXN];
int sum, i, j, n, tmp;
Inite();
scanf("%d", &n);
while(n--)
{
scanf("%s", str);
sum = 0;
for(i = 0; i < MAXN; ++i)
{
tmp = 0;
for(j = i+1; j < MAXN; ++j)
{
if(str[i] > str[j])
tmp++;
}
sum += tmp*fac[11-i];
}
printf("%d\n", sum+1);
}
return 0;
}