康托展开和逆康托展开

为什么我在EN-WIKI上查不到啊TAT

嗯,康托展开就是一个从n排列集合到自然数集合的映射.

并且这个映射刚好对应了字典序.

比如,设这个函数为C,

那么C{1,2,3}=0,C{3,2,1}=5,C{2,3,1}=3.

用于排列与数字的转换.


使用的时候一定要注意先把排列离散化成0,1,...,n-1的排列再做.

否则要用n^2的时间来找"在全集中比数i小的数个数." 离散化以后直接就是a[i]+1就好做一些.


离散化之后可以用树状数组或者线段树或者平衡树做到nlogn.


#include <cstdio>
#include <iostream>
#include <fstream>

#include <cmath>
#include <cstdlib>
#include <algorithm>

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;

int getint()
{
	int res=0; char c=getchar(); bool m=false;
	while(c<'0' || c>'9') m=(c=='-'),c=getchar();
	while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
	return m ? -res : res;
}

const db eps=1e-18;
bool feq(db a,db b)
{ return fabs(b-a)<eps; }

using namespace std;




int a[50]={0,1,2,3,4,5};

int getpow(int i)
{
	int res=1;
	while(i>0) res*=i,i--;
	return res;
}

int Cantor(int*a,int n)
{
	int res=0;
	for(int i=0;i<n;i++)
	{
		int cnt=0;
		for(int j=0;j<i;j++) if(a[j]<a[i]) cnt++;
		res+=getpow(n-i-1)*max(0,a[i]-cnt);
	}
	return res;
}

bool used[50];
int*RevCantor(int X,int n)
{
	int*res=new int[n+1];
	
	memset(used,0,sizeof(bool)*(n+1));
	for(int i=0;i<n;i++)
	{
		int p=X/getpow(n-i-1);
		X=X-p*getpow(n-i-1);
		
		int j=0;
		for(;used[j]||p>0;j++)
		if(!used[j]) p--;
		
		res[i]=j;
		used[j]=true;
	}
	
	return res;
}

int main()
{
	for(int i=0;i<10;i++)
	{
		int c=Cantor(a,6);
		cout<<i<<' '<<c<<endl;
		for(int i=0;i<6;i++) cout<<a[i]<<' '; cout<<endl;
		next_permutation(a,a+6);
		int*p=RevCantor(c,6);
		for(int i=0;i<6;i++) cout<<p[i]<<' '; cout<<endl;
		delete(p);
	}
	
	
	return 0;
}

posted @ 2015-02-18 13:59  DragoonKiller  阅读(404)  评论(0编辑  收藏  举报