康託逆展開式

關於康託展開式的問題我已經在第一篇文章提到了,需要的朋友可以點進去看看。
ACM題目之排列序數
康託逆展開式就是給你一個數組,然後讓你求出其全排列第n大的序列.
這裏借用百度百科給的大家解釋下
例1 {1,2,3,4,5}的全排列,並且已經從小到大排序完畢
(1)找出第96個數
首先用96-1得到95
用95去除4! 得到3餘23
有3個數比它小的數是4
所以第一位是4
用23去除3! 得到3餘5
有3個數比它小的數是4但4已經在之前出現過了所以第二位是5(4在之前出現過,所以實際比5小的數是3個)
用5去除2!得到2餘1
有2個數比它小的數是3,第三位是3
用1去除1!得到1餘0
有1個數比它小的數是2,第二位是2
最後一個數只能是1
所以這個數是45321
(2)找出第16個數
首先用16-1得到15
用15去除4!得到0餘15
用15去除3!得到2餘3
用3去除2!得到1餘1
用1去除1!得到1餘0
有0個數比它小的數是1
有2個數比它小的數是3 但由於1已經在之前出現過了所以是4(因爲1在之前出現過了所以實際比4小的數是2)
有1個數比它小的數是2 但由於1已經在之前出現過了所以是3(因爲1在之前出現過了所以實際比3小的數是1)
有1個數比它小得數是2 但由於1,3,4已經在之前出現過了所以是5(因爲1,3,4在之前出現過了所以實際比5小的數是1)
最後一個數只能是2
所以這個數是14352。
code:

//康託逆展開式
//求n個數的全排列中第m大的組合:
//n>20就需要用高精度來做
#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int fact[10]= {1};
int res[10];
int n,m;
int main()
{
    for(int i=1; i<=10; ++i)
        fact[i]=fact[i-1]*i;  //階乘數組
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        bool *vis=new bool[n];
        memset(res,0,sizeof(res));
        memset(vis,false,sizeof(vis));
        //先判斷下m是否比n!還要大
        if(m>fact[n]){cout<<"impossible"<<endl;continue;}
        int i,j,x,t;
        m--;
        for(i=1; i<=n; ++i)
        {
            x=m/fact[n-i];
            m%=fact[n-i];
            for(j=1,t=0; t<=x; j++)
                if(!vis[j])t++;
            j--;
            res[i-1]=j;
            vis[j]=true;
        }
        for(i=0; i<n; ++i)
            printf("%d",res[i]);
        printf("\n");
    }
    // cout << "Hello world!" << endl;
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章