康托展开

1.康托展开(按照字典序即从小到大生成排列)

a) 康托展开的公式是 X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!其中,ai为当前未出现的元素中是排在第几个(从0开始)。

b) an为小于该元素的值有几个

i. 假设有”abcdefghijkl”12个字符(初始时为字典序)

1. hgebkflacdji为上述字符的一个排列

a) 所以a(12) = 7, a(11) = 6, a(10) = 4,1,6,3,5,0,0,0,1,0

b) ai(1 < i <= n)的值是怎么求的呢?如:a(12) = 7表示在hgebkflacdji排列中比h小的有7个:abcdefg; 

a(11) = 6表示在gebkflacdji排列中比g小的有6个:abcdef; 

a(10) = 4表示在ebkflacdji排列中比e小的有:abcd。  后面依次类推得出结果。

我排第几个南理139题

时间限制:1000 ms  |  内存限制:65535 KB

难度:3

描述

现在有"abcdefghijkl”12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的?

输入

第一行有一个整数n(0<n<=10000);
随后有n行,每行是一个排列;

输出

输出一个整数m,占一行,m表示排列是第几位;

样例输入

3

abcdefghijkl

hgebkflacdji

gfkedhjblcia

样例输出

1

302715242

260726926

#include<stdio.h>

char str[13];

 

long factorial(int n) {

  long sum = 1;

  for (int i = 2; i <= n; i++) sum *= i;

  return sum;

}

 

int main() {

  int t;

  scanf("%d", &t);

  while (t--) {

    int count;

    long sum = 0;

    int k = 11;

    scanf("%s", str);

    for (int i = 0; i < 12; i++) {

      count = 0;

      for (int j = i+1; j < 12; j++) {

        if (str[i] > str[j]) count++;

      }

      sum += count*factorial(k);

      k--;

    }

    printf("%ld\n", sum+1);

  }

  return 0;

}

2.康托展开的逆运算可以生成全排列

a) abcd的20个排列

i. 20/(3!) = 3 余 2

ii. 2/(2!) = 1 余 0

iii. 0/(1!) = 0 余 0

iv. 0/(0!) = 0 余 0

b) 此排列为dbac

i. 3个比它小的是d

ii. 1个比它小的是b

iii. 0个比它小的是a

iv. 0个比它小的是c

c) 代码实现


 代码1:

#include<iostream>

#include<vector>

#include<algorithm>

#include<cstring>

#include<cstdio>

using namespace std;

 

char str[10];

int len;

string s;

 

int factorial(int n) {

  int sum = 1;

  for (int i = 2; i <= n; i++) sum *= i;

  return sum;

}

 

long position() {//求一个排列的位置

  long pos;

  int count;

  vector<int> vec(10005);

  

  for (int i = 0; i < len; i++) {

    count = 0;  

    for (int j = i+1; j < len; j++)  

      if (str[i] > str[j]) count++;

    vec[len-i-1] = count;

  }

  pos = 0;

  for (int i = 0; i < len; i++)  

    pos += vec[i]*factorial(i);

  return pos;

}

 

void print(int pos) {

  vector<char> num;

  for (int i = 0; i < len; i++) num.push_back(str[i]);

  div_t divresult;

  for (int i = len-1; i >= 0; i--) {

    divresult = div(pos, factorial(i));//求余数与除数

    s.push_back(num[divresult.quot]);

    num.erase(num.begin() + divresult.quot);

    pos = divresult.rem;//余数

  }

}

 

int main() {

  scanf("%s", &str);

  len = strlen(str);

  printf("%ld\n", position()+1);//输出一个排列的位置

  print(302715241);//打印该位置的排列 位置序数从0开始

  cout << s << endl;

  return 0;

}


代码2:(此代码来自:http://blog.youkuaiyun.com/zhongkeli/article/details/6966805)

#include<iostream>  

#include<algorithm>  

#include<vector>  

#include<cstdlib>  

using namespace std;  

 

class cantor {

public:  

    int n;//字符串的长度  

    string s;  

    int pos;//字符串在全排列中的字典位置,从0开始  

    vector<char> num;//所有的字符  

    cantor(string s):s(s) {n = s.size();}  

    cantor(int n, int pos):n(n), pos(pos) {

      int i;

      for (i = 0; i < n; i++)  

        num.push_back(i+65);

    }

    int fac(int);

    void encode();

    void decode();

};

 

int cantor::fac(int num) {  

    if (num == 0) return 1;  

        else return num * fac(num-1);  

}

 

void cantor::encode() {

    int i, j, count;  

    vector<int> vec(n);  

    for (i = 0; i < n; i++) {  

      count = 0;  

      for (j = i; j < n; j++)  

        if (s[i] > s[j]) count++;     

        vec[n-i-1] = count;

    }

    pos = 0;

    for (i = 0; i < s.size(); i++)  

      pos += vec[i]*fac(i);

}

 

void cantor::decode() {  

    int i;

    div_t divresult;

    for (i = n-1; i >= 0; i--) {

      divresult = div(pos, fac(i));//求余数与除数

      s.push_back(num[divresult.quot]);

      num.erase(num.begin() + divresult.quot);

      pos = divresult.rem;//余数

    }

}

 

int main(){

  for (int i = 0; i < 24; i++) {

    cantor test(4, i);

    test.decode();  

    cout << test.s << endl;

  }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值