康拓展开,逆展开(全排列求第n个)

定义: 实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。

解决问题: 举例由12345–>34251,问34251是全排列的第几个?(按字典序小的开始全排列)

计算方法: 首先我们可以发现由12345–>25431是比较好计算全排列数的,也就可以看成是算由12345–>15432(需要4!次),由21345–>25431(也需要4!次),所以12345–>25431需要2 * 4!次变换,然后我们接着算31245–>32541的全排列数,可以分解成由31245–>31542(需要3!次),和32145–>32541(需要3!次),合起来2 * 3!次,然后12345–>32541的变换次数就是两种情况相加,即2 * 4!+2 * 3!,然后继续算34125–>34152,也就是2!,然后继续算34215–>34215,就是1!,而我们最后一个34251不计算入其中(因为照康拓展开的意思就是这样),那么我们不难发现12345–>34215的变换次数可以合并为2 * 4!+2 * 3!+1 * 2!+1 * 1!+0 * 0!,然后找一波规律,就可以发现刚刚计算得到的全排列个数x,可以表示成x=a[n] * (n-1)!+a[n-1] * (n-2)!+…a[1] * 0!,(a[i]代表第i位数(从右向左数)更右边的数中比第i位数小的数总和)。又由于对于例如计算12345–>34251,34251是全排列第几个的问题,康拓展开求得的是34215是全排列第几个数,所以最后算出的x还需+1才是最终结果。

主要代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ll Cantor(string s,int n){
	ll ans=0;
	for(int i=0;i<n;i++){
		ll cnt=0,m=1,temp=1;
		for(int j=i+1;j<n;j++){
			if(s[i]>s[j])
			cnt++;//计后边小于第i个数的个数
			m*=temp,temp++;//m最终保存阶乘结果
		}
		ans+=cnt*m;
	}
	return ans+1;
}

康拓逆展开择日再写。。。 to be continued……

comments powered by Disqus