康拓展开与逆康拓展开

版权声明:作者:carver 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 https://blog.csdn.net/Victor_zero/article/details/53927801

康托展开与逆康托展开

  康托展开据我所理解,应该便是给你一个已知的排列,然后这个排列在全排列中是第几小或者第几大的,一般都是按字典序计算,即为第几小的。康托展开的公式为:X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0!  并且,其中a[i]就是按字典序当前没有出现的比a[i]小的字符的个数,最后算出来的X仅仅只是比当前排列小的排列的个数,若是要算第几小,还要加上1,下面举例解释说明:
  假如现在有一个排列为 bdca 求它在{a,b,c,d}的全排列中为第几小
  那么 比b小的且当前未出现的有a有一个,所以为1,再来看第二个字母d,比d小的有a,b,c,但b已经出现过了,所以为2,以此类推,最终算得,X = 1 * 3! + 2 * 2! + 0 * 1! + 0 * 0! = 10
  所以,bdca 为第11个排列在{a,b,c,d}组成的全排列中。
  本算法若想联系可以搜索关键字“南阳理工ACM 139我排第几”,下面附上c语言代码实现:
#include <stdio.h>
#define N 12
const int factorial[12] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800};
int function(char str[]);

int main(void)
{
	int n;
	char string[15];
	scanf("%d",&n);
	while(n--)
	{
		scanf("%s",string);
		printf("%d\n",function(string)+1);
	}
	return 0;
}

int function(char str[])
{
	int i,j,rev,sum;
	rev = sum = 0;
	for (i = 0;i < N;i++)
	{
		rev = 0;
		for (j = i+1;j < N;j++)
			rev += (str[i] > str[j]);
		sum += rev * factorial[N-i-1];
	}
	return sum;
}

  而逆康托展开则可以望文生义,便是康托展开的逆运算,也同样可以代码实现。有兴趣的可以搜索关键字“南阳理工ACM 143我是第几”。下面附上代码:
#include <stdio.h>
#include <string.h>
const int factorial[12] = {1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800};
int main(void)
{
    int m,n;
	int i,j,k;
	int adj[12];
	char str[13];
	scanf("%d",&n);
	while (n--)
	{
		memset(adj,0,sizeof(adj));
		scanf("%d",&m);
		--m;
		for (i = 11;i >= 0;i--)
		{
			j = -1;
			for (k = 0;k <= m/factorial[i];k++)
				while (adj[++j]);

			adj[j] = 1;
			str[11-i] = 'a' + j;
			m = m % factorial[i];
		}
		str[12] = '\0';
		printf("%s\n",str);
	}
	return 0;
}

以上代码均为C语言

猜你喜欢

转载自blog.csdn.net/Victor_zero/article/details/53927801