算法之我见 [康托展开] 排列编号

问题描述:对于1~N的序列来说,将他的全排列升序编号,问第n排列是怎样的,或者给一个排列问是第几个序列?

关键思想:这种问题可以用康托展开来解决,其关键思想其实很简单,说白了就是简单的排列组合。

我们先来写下1~4的全排列感受一下{

  1 2 3 4

       1 2 4 3

       1 3 2 4

       1 3 4 2

       1 4 2 3

       1 4 3 2

然后依次枚举2、3、4开头的,不多写了。

}

我们想一下,枚举的时候是在前几位尽量找小的开始枚举,确定该位后,下一位也要尽可能小,就这样枚举。

于是有这样一种方法来算出序号。

我们找到第i位,在右边剩下N-i位数数有几个比第i位小的,这些小的在第i位的排列此前已经历数过了,很关键,我们就加上去。

核心思想就是这个了。确定i位的时候,剩下N-i位有(N-i)!种排列。

代码如下:

#include <iostream>
#include <string>
using namespace std;

const int MAXN=10;
int fac[10]={1,1,2,6,24,120,720,5040,40320,362880};

int EnKT(string s,int N){
    int ans=1,cnt;
    for(int i=0;i<N;i++){
        cnt=0;
        for(int j=i+1;j<N;j++){//找到后面比较小的数,他们在i处的排列已经历数过
            if(s[j]<s[i])cnt++;
        }
        ans+=cnt*fac[N-i-1];
    }
    return ans;
}

string DeKT(int N,int n){//N的排列第n个序列
    string ans="";
    n--;
    bool vis[MAXN]={false};
    for(int i=N-1;i>=0;i--){
        int order=n/fac[i];//0对应最小未使用;1对应第二大未使用
        for(int j=1;j<=N;j++){
            if(!vis[j]){
                if(order==0){
                    ans+='0'+j;
                    vis[j]=true;
                    break;
                }else order--;
            }
        }
        n%=fac[i];
    }
    return ans;
    

}

int main(){
    int n;
    string t;
    cin>>n>>t;
    cout<<EnKT(t,n)<<endl;
    cout<<DeKT(5,7)<<endl;
    return 0;
}

 

    原文作者:哇咔咔咔
    原文地址: https://www.cnblogs.com/G-M-WuJieMatrix/p/7423268.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞