康托展开和逆展开

来源:互联网 发布:java三目运算符怎么用 编辑:程序博客网 时间:2024/05/16 23:39

康托展开:

对于1~n的所有排列,要确定某个排列是字典序中第几个排列,可用康托展开。这个技巧在做对排列的hash时十分有用,因为不需要使用set来记录那些大于int最大值的数字了。

原理十分简单,对于4 5 1 3 2 这个排列来说,第一位是4,大于(1,2,3)3个数,以这三个数开头的排列共有3*4!个,它们都小于原排列;再看第二位5,在这一位上有4个数小于5,但是由于现在考虑的情况是第二位前都和原排列相同,所以4不能放在这里,因此又有3*3!个排列小于原排列,以此类推一直处理下去,最后得到的答案是从0开始的。

代码:

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#define LL long longint a[100];LL fac[100];int main(){    int n;    fac[0]=1;    for(int i=1;i<=9;i++) fac[i]=fac[i-1]*i;    scanf("%d",&n);    int res=0;    for(int i=0;i<n;i++) scanf("%d",&a[i]);    for(int i=0;i<n;i++){        int k=0;//统计有多少可以排在第i位,且比a[i]小的        for(int j=i+1;j<n;j++) if(a[j]<a[i]) ++k;        res+=k*fac[n-1-i];    }    printf("是第%d个排列\n",res+1);}

康托逆展开:

给定排列的序号,求排列。

从第一位开始逐个确定排列的元素。

以5位排列中第66个排列为例:

66-1=65(排列号应从0开始)

用65除 4! = 2,有两个小于第一位的数,因此第一位为3。 65%4!=17

用17除 3! = 2,有两个小于第二位的数,由于第二位前的数已经确定,不能放在第二位,所以3不可能在第二位,第二位为4。

以此类推


代码:

#include <iostream>#include <cstdio>#include <algorithm>using namespace std;#define LL long longbool vis[100];LL fac[100];int a[100];int main(){    int n;    fac[0]=1;    for(int i=1;i<=9;i++) fac[i]=fac[i-1]*i;    scanf("%d",&n);    int ord;    scanf("%d",&ord);    ord--;    for(int i=0;i<n;i++){        int t=ord/fac[n-1-i]+1;//+1转化为有t个数小于等于a[i]        ord%=fac[n-1-i];        int k=0;        int j;        for(j=1;j<=n;j++){            if(!vis[j]) k++;            if(k==t) break;        }        a[i]=j; vis[j]=1;    }    for(int i=0;i<n;i++) printf("%d ",a[i]);    printf("\n");}



0 0
原创粉丝点击