17AHU排位赛3 A题 (逆康托展开)

problem

输入两个整数n和k,询问1~n的所有排列中,按字典序升序排在第k位的排列是什么

Input

输入两个整数n,k(2<=n<=18,1<=k<=n!)

Output

输出n个整数,表示字典序排在第k位的那个排列。

Input

3 4

Output

2 3 1

Limitation

1s 256MB

Hint

n=3的时候,所有的排列按照字典序升序排列如下:
[1,2,3]
[1,3,2]
[2,1,3]
[2,3,1]
[3,1,2]
[3,2,1]
所以第4位的排列是[2,3,1]


传送门传送门传送门传送门传送门


思路

康托展开:它是一个式子 与全排列有关
X=a[n](n-1)!+a[n-1](n-2)!+…+a[i]*(i-1)!+…+a[1]*0! n为排列元素个数
其中a[i]为当前未出现的元素中是排在第几个(从0开始计数)

例如有一个数组 s = [“A”, “B”, “C”, “D”]
它的一个排列 s1 = [“D”, “B”, “A”, “C”] 现在要把s1序列映射出对应的X
所以 n=4 X(s1) = a4*3! + a3*2! + a2*1! + a1*0!
a4为D a3为B a2为A a1为C
a4=3 a3=1 a2=0 a1=0(a1总是0) (从零计数)
X(s1) = 3*3! + 1*2! + 0*1! + 0*0! = 20


康托展开逆运算:如果已知 s = [“A”, “B”, “C”, “D”],X(s1) = 20,能否推出 s1 = [“D”, “B”, “A”, “C”] 呢?

因为已知 X(s1)=a43!+a32!+a21!+a10!=20 ,所以问题变成由 20 能否唯一地映射出一组 a4、a3、a2、a1?如果不考虑 ai 的取值范围,有:
33!+12!+01!+00!=20
23!+42!+01!+00!=20
13!+72!+01!+00!=20
03!+102!+01!+00!=20
03!+02!+201!+00!=20

但是满足 0 <= ai <= n-1 的只有第一组,可以使用辗转相除的方法得到 ai

这里写图片描述


知道了a4、a3、a2、a1的值,就可以知道s1[0] 是子数组[“A”, “B”, “C”, “D”]中第3大的元素 “D”,s1[1] 是子数组 [“A”, “B”, “C”] 中第1大的元素”B”,s1[2] 是子数组 [“A”, “C”] 中第0大的元素”A”,s[3] 是子数组 [“C”] 中第0大的元素”C”,所以s1 = [“D”, “B”, “A”, “C”] (第几大从零计数)


代码示例

#include<bits/stdc++.h>
using namespace std;

long long f[20];//阶乘

void init()
{
    f[0]=1;
    f[1]=1;
    for(int i=2;i<=19;++i)
        f[i]=f[i-1]*i;
}

int main()
{

    ios::sync_with_stdio(false);
    init();
    //cout<<f[18]<<endl;
    long long n,k;//n是位数,k是第几个
    long long v[20],num[20],i,j,t;
    cin>>n>>k;
    k--;
    memset(v,0,sizeof(v));
    for(int i=0;i<n;++i){
        t=k/f[n-1-i];
        for(j=0;j<n;++j){
            if(!v[j]){
                if(t==0) break;
                t--;
            }
        }
        num[i]=j;
        v[j]=1;
        k=k%f[n-1-i];
    }
    for(int i=0;i<n-1;++i) cout<<num[i]+1<<' ';
    cout<<num[n-1]+1<<endl;
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值