今天写题又碰到全排列的问题,又是学习新知识的一天。
先说一下题目大意
题目描述
给定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;
}
好了好了,还在练习写博客的路上,先写这么多吧!大家一起进步呀!