康托展开的小讲

今天写题又碰到全排列的问题,又是学习新知识的一天。
先说一下题目大意

题目描述

给定n与一个数列,要求求出给定数列在n的全排列中的排名(按照字典序从小到大排列)

刚看到这题思路也就是暴力呗,循环呗,没有一点骚气的想法,只怪自己太菜哈哈。然后偷偷看了题目的算法标签,康托展开! what????这是什么名词,没见过。问题不大,找度娘。

康托展开概念

康托展开是一个全排列到一个自然数的双射,常用于构建哈希表时的空间压缩。
康托展开的实质是计算当前排列在所有由小到大全排列中的顺序,因此是可逆的。
双射的概念,对不起,我模模糊糊知道,想彻底了解需要大家自行百度,今天的重点是康托!!!
概念是不是不好理解,那我直接说一下它到底是干什么的。康托展开的作用是求n个数的全排列中某一个序列在所有排列中的次序(该排列次序(亦称之为排名)以字典序从小到大排序)

康托展开公式

X=a[n](n-1)!+a[n-1](n-2)!+…+a[1]*0!。
这里X就是全排列某个序列在所有排列中的次序减去1
0<=a[i]<=i,0<=i<n,表示当前未出现的元素排第几个(a[i]代表当前排列里从i位置右侧比i位置的数小的数的个数)

说了这么多,我觉得没有例子来说概念是没有灵魂的,哎嘿,不知道大家是喜欢什么,我是喜欢从例子中来理解概念,因为简单啊!

例子

在n=3的全排列中,132排第几位。
可以写出n=3的全排列 123,132,213,231,312,321
(在这里我们按照字典序从小到大排序)
所以容易看出132排在第二位。
那么如何用康托来解释呢?
已知n=3
1、 首位是 1,小于1的个数是0,所以a[3] = 0,那么首位小于1的所有排列组合为a[3](3-1)!
2、第二位是3,小于3的数有1个,为2,注意这里1并不能算,1已经在第一位了,所以计算的是在第二位之后小于3的数 所以a[2]=1
3、最后一位不用算了,后边都没数了,所以a[1]=0
那么由公式可得
X = a[3](3-1)! + a[2](3-2)! + a[1](3-3)!
**
代入得X=1,注意不要高兴得太早,结果要加1才是真正的结果!

例题网址

https://www.luogu.com.cn/problem/P2524

代码块,有兴趣的同学可以看看

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1000;
typedef long long ll;
int b[10] = {1,1,2,6,24,120,720,5040,40320,362880};
char a[15];
int main()
{
 ios::sync_with_stdio(false);
 int n;
 cin >> n;
 int count = 0;
 for(int i = 0;i<n;i++)
 {
  cin >> a[i];
 }
 for(int i = 0;i<n;i++)
 {
  int sum = 0;
  for(int j = i+1;j<n;j++)
  {
   if(a[j] < a[i])
    sum ++;
  }
  count += b[n-i-1]*sum;
 }
 cout << count+1;
 cout << endl;
 return 0;
}

好了好了,还在练习写博客的路上,先写这么多吧!大家一起进步呀!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值