康托展开
问:给你一个n个数的数串(一般来说小于等于16),其中的数只出现一次,求在这些数的全排列中该数串是第几大(或第几小)的。
由这个问题引出了康托展开这种方法
1.公式
把一个整数X展开成如下形式:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0!
其中,a为整数,并且0<=a[i]<i(1<=i<=n)
2.适用范围
每个数(不一定是数字)只出现一次的判重,经典的是被用在解决8数码问题上。
压缩状态用,9位压缩为
3.举例说明
例1:{1,2,3}的全排列有123,132,213,231,312,321(注:该顺序即从小到大排的顺序)
比如求231在这个序列中的位置。
从后面开始
对于每个数向后找比他小的数
1后面没有比他小的数,0*0!=0 num=0
3后面有一个比他小的数1,1*1!=1 num=1
2后面有一个比他小的数1,1*2!=2 num=3
寻找完毕,还需要一步的处理是num=num+1=4
得解是231是第四小的数
(注解:为num=num+n*k!的形式,n表示右边比当前位置数小的的数的个数,k表示该位置对应的阶乘数,
注意k是比当前位置i小1的。
关于这里的+1这个步骤,是为了符合求解的问题,
解释一下,很简单,还是{1,2,3},
如果给出的数串是最小的123,
那么,经过上面的流程后会得到0,num+1后正好表示该数列在全排列中排第1小,
同理都要加1得问题的解。
其实展开流程中求得的num是比这个数串小的数串的个数)
例2:对1324求位置
对4 0*0!=0
对2 0*1!=0
对3 1*2!=2
对1 0*3!=0
num=2 num=num+1=3
则1324是第三小的数。
4.代码
写得不好看,而且是只针对9位以下的单位数数串处理的程序,仅提供参考。
(表示我是pascal语言)
1 var
2 i,j,n,js,m,mm,k,l,z:longint;
3 t,s,num:array[1..100]of longint;
4 begin
5 readln(n);
6 t[1]:=0;t[2]:=1;
7
8 for i:=3 to 9 do
9 t[i]:=t[i-1]*(i-1);
10
11 while n>0 do
12 begin
13 inc(l);
14 num[l]:=n mod 10;
15 n:=n div 10;
16 end;
17
18 for i:=2 to l do
19 begin
20 js:=0;
21 for j:=1 to i-1 do
22 if num[j]<num[i] then inc(js);
23 mm:=mm+js*t[i];
24 end;
25 writeln(mm+1);
26 end.