关于数论【康托展开及其逆运算】

表示这个东西背了很多次,但是次次忘,希望这次能够记住吧。

康托展开:
问45231是n=5的全排列中第几个排列?
ans:= 3*4! + 3*3! + 1*2! + 1*1! + 0*0! =93
这时求出的是在45231前面全部的排列,排名还要加1
所以对此的做法,就是将阶乘前面的求出来,这个就是在a[i]前面,还没出现过的数字。比如4前面1~3都没出现而1(或2或3)xxxx肯定在4xxxx前面,因为有四个不定的数字,所以乘上4!

逆运算:
问n=5的全排列中第94个是谁?94先-1
94/4!=3.875
所以第一个数字前面有3个数字
94先减3*4!=22
22/3!=3.6666666666666666666666666666667
前面还是有三个,因为4之前用过了,所以是5
由此类推。

模板题caioj1220:

#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
int n,a[20];
LL jc[20];
bool bo[20];
void kangtuo1()
{
    jc[0]=1;for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        jc[i]=jc[i-1]*i;
    }
    LL ans=0;//有多少个这个全排列前面的
    memset(bo,false,sizeof(bo));//这个有没有在前面出现过
    for(int i=1;i<=n-1;i++)
    {
        int k=0;//k表示前面有多少个没被访问过的
        for(int j=1;j<a[i];j++)
            if(bo[j]==false)k++;
        bo[a[i]]=true;//当前这个数被访问过
        ans+=k*jc[n-i];//乘以当前个数的阶乘
    }
    printf("%lld\n",ans+1);
}
void kangtuo2()
{
    LL ans;
    scanf("%lld",&ans);ans--;
    memset(bo,false,sizeof(bo));
    for(int i=1;i<=n;i++)
    {
        LL k=ans/jc[n-i];//有多少个比第i个位置小的数
        ans-=k*jc[n-i];
        for(int j=1;j<=n;j++)
            if(bo[j]==false)
            {
                if(k==0)
                {
                    a[i]=j;
                    bo[a[i]]=true;
                    break;
                }
                k--;
            }
    }
    for(int i=1;i<n;i++)printf("%d ",a[i]);
    printf("%d\n",a[n]);
}
int main()
{
    scanf("%d",&n);
    kangtuo1();
    kangtuo2();
    return 0;
}
时间: 2024-10-18 22:01:15

关于数论【康托展开及其逆运算】的相关文章

康托展开及其逆运算 详解

前文: 这个东东是我准备进攻一道A*算法的八数码题目时,遇到的. 决定先搞懂这个,再进攻八数码(传说中  不做人生不完整的 题目). 康托展开是什么? 定义: X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0! ai为整数,并且0<=ai<i(1<=i<=n) 简单点说就是,判断这个数在其各个数字全排列中从小到大排第几位. 比如 132,在1.2.3的全排列中排第2位. 康托展开有啥用呢? 维基:n位(0~n-1)全排列后,其

poj 1077 八数码(BFS+康托展开)

1 /* 2 题意:八数码问题,给出3*3的矩阵含1~8以及x,给出一个符合的解使得移动后的矩阵的顺序为1~8,最后为x 3 4 题解:BFS 5 需要用到康托展开来表示状态,不然数组无法完全表示所有状态,这样BFS就无法判断找不到解的情况(status 6 的0ms,0KB究竟是怎么做到的,简直不能想象=.=) 7 */ 8 #include <cstdio> 9 #include <cstring> 10 #include <queue> 11 #include &

NYOJ143 第几是谁? 【逆康托展开】

第几是谁? 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 现在有"abcdefghijkl"12个字符,将其按字典序排列,如果给出任意一种排列,我们能说出这个排列在所有的排列中是第几小的.但是现在我们给出它是第几小,需要你求出它所代表的序列. 输入 第一行有一个整数n(0<n<=10000); 随后有n行,每行是一个整数m,它代表着序列的第几小: 输出 输出一个序列,占一行,代表着第m小的序列. 样例输入 3 1 302715242 2607

康托和逆康托展开(转)

1.康托展开的解释 康托展开就是一种特殊的哈希函数 把一个整数X展开成如下形式: X=a[n]*n!+a[n-1]*(n-1)!+...+a[2]*2!+a[1]*1! 其中,a为整数,并且0<=a<i,i=1,2,..,n {1,2,3,4,...,n}表示1,2,3,...,n的排列如 {1,2,3} 按从小到大排列一共6个.123 132 213 231 312 321 . 代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来. 他们间的对应关系可由康托展开来找到.

康托展开:对全排列的HASH和还原,判断搜索中的某个排列是否出现过

题目:http://acm.hrbust.edu.cn/index.php?m=ProblemSet&a=showProblem&problem_id=2297 前置技能:(千万注意是从0开始数的 康托展开表示的是当前排列在n个不同元素的全排列中的名次.比如213在这3个数所有排列中排第3. 那么,对于n个数的排列,康托展开为: 其中表示第i个元素在未出现的元素中排列第几.举个简单的例子: 对于排列4213来说,4在4213中排第3,注意从0开始,2在213中排第1,1在13中排第0,3在

Gym10081 A - Arcade Game -康托展开、全排列、组合数变成递推的思想

最近做到好多概率,组合数,全排列的题目,本咸鱼不会啊,我概率论都挂科了... 这个题学到了一个康托展开,有点用,瞎写一下... 康托展开: 适用对象:没有重复元素的全排列. 把一个整数X展开成如下形式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0![1] 其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]<i(1<=i<=n) 用来求全排列中这个串排第几,康托展开的逆运算就是

浅谈康托展开和其逆运算

康托展开,是一种在\(\mathcal{O}(n^2)\)(\(n\)为排列元素个数)时间复杂度求解某一排列在全排列中的次序的算法. 我们以一道例题引入: 排列的序号 题目描述: 给定一个数\(n\)和一个\(n\)个数的排列\(a\),求\(a\)在\(n\)的全排列中的序号. 输入描述: 第一行一个整数\(n\),第二行一个排列\(a\). 输出描述: 求\(a\)在\(n\)的全排列中的序号. 输入输出样例: 输入 3 123 输出 1 数据范围 \(n\le 15\) 根据排列组合.加法

nyist 139 我排第几个&amp;&amp;143 第几是谁(康托展开和逆康托展开)

 我排第几个 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 现在有"abcdefghijkl"12个字符,将其所有的排列中按字典序排列,给出任意一种排列,说出这个排列在所有的排列中是第几小的? 输入 第一行有一个整数n(0<n<=10000); 随后有n行,每行是一个排列: 输出 输出一个整数m,占一行,m表示排列是第几位: 样例输入 3 abcdefghijkl hgebkflacdji gfkedhjblcia 样例输出 1 3027

LightOJ1060 nth Permutation(不重复全排列+逆康托展开)

一年多前遇到差不多的题目http://acm.fafu.edu.cn/problem.php?id=1427. 一开始我还用搜索..后来那时意外找到一个不重复全排列的计算公式:M!/(N1!*N2!*...*Nn!), 然后就靠自己YY出解法,搞了好几天,最后向学长要了数据,然后迷迷糊糊调了,终于AC了. 后来才知道当时想的解法类似于逆康托展开,只是逆康托展开是对于没有重复元素全排列而言,不过有没有重复元素都一个样. 而现在做这题很顺,因为思路很清晰了,另外这做法和数论DP的统计部分有相似之处.