对于n个数的全排列,共有n!中排列方式,如何求某一个序列在整个排列中的次序(从小到大)?
以9的全排列举例:842697513是1-9全排列的第几个?(高中数学排列组合问题,只需要做到不重不漏)
首先看第一位为8,那么第一位为1-7的全排列都比它小,共有7*8!个。
在第一位为8的情况下,其次看第二位为4,那么第二位为1-3的全排列都比它小,共有1*3*7!个。
在第一位为8,第二位为4的情况下,那么第三位为1的全排列都比它小,共有1*1*6!个。
在第一位为8,第二位为4,第三位为2的情况下,那么第四位为1-5的全排列都比它小,这里由于第二位4和第三位2已经确定,第四位的可能取值只能为(1,3,5),共有1*1*1*3*5!
在第一位为8,第二位为4,第三位为2,第四位为6的情况下,那么第五位为1-8的全排列都比它小,这里由于第二位为4,第三位为2,第四位为6已经确定,第五位的可能取值只能为(1,3,5,7),共有1*1*1*1*4*4!
......
对于第i位的可能取值,前i位的数字已经确定,且在全排列中每一个数字只能出现一次,只需要比较i位后面的数字比i位数字小的有几个,即可知道第i位的可能取值。
总结上面的方法,计算1-n的全排列
a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!
康托展开
康托展开
康托展开就是一种特殊的哈希函数,它的使用范围是对于n个数的排列进行状态的压缩和存储,例如要对9的全排列进行判重.没有必要
开一个10^9的数组,同时内存也不允许开到那么大的数组.对此,有人提出了优化,即对于一个n的排列数,没有必要开到10^n,因为在一个排列中每个数只出现一次,所以只要前n-1位确定了,前N位就确定了.
但是以上的想法仍不是可行的,因为N可以很大,例如15,所以便引入了康托展开:只需要确定这个排列在总的排列情况中是第几小的就可以了.
例如:我想知道321是{1,2,3}的排列中第几个大的数可以这样考虑第一位是3,当第一位的数小于3时,那排列数小于321 如 123 213 小于3的数有1,2 所以有2*2!个 再看小于第二位2的小于2的数只有一个就是1 所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个所以321是第6个大的数。 2*2!+1*1!是康托展开.
我排第几个
-
描述
-
现在有"abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的?
-
输入
-
第一行有一个整数n(0<n<=10000);
随后有n行,每行是一个排列;
输出
- 输出一个整数m,占一行,m表示排列是第几位; 样例输入
-
3 abcdefghijkl hgebkflacdji gfkedhjblcia
样例输出
-
1 302715242 260726926
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
using namespace std;
int power[13];
void db()
{
power[0]=power[1]=1;
for(int i=1; i<=12; i++)
power[i]=power[i-1]*i;
}
int main()
{
int ncase;
cin>>ncase;
db();
getchar();
while(ncase--)
{
char c[30];
cin.getline(c,30);
int l=strlen(c);
long long sum=0;
for(int i=0; i<l; i++)
{
int tt=0;
for(int j=l-1; j>i; j--)//从后向前扫描,寻找比自己小的,就是比c[i]位置小的几种情况
if(c[j]<c[i])
{
tt++;
}
//然后在乘以后面所有位数的全排列
sum+=tt*power[l-i-1];
}
cout<<sum+1<<endl;
}
}